我正在学习 Haskell。如果我理解正确,Haskell 中的一个简单函数始终是引用透明的。我认为这意味着它的输出仅取决于传递给它的参数。
但是一个函数f
可以调用另一个函数g
,定义在外部范围内。所以从这个意义上说,f
的返回值取决于 的定义g
。并且该函数g
没有作为参数传递f
- 至少没有明确传递。这不会破坏引用透明度吗?
我正在学习 Haskell。如果我理解正确,Haskell 中的一个简单函数始终是引用透明的。我认为这意味着它的输出仅取决于传递给它的参数。
但是一个函数f
可以调用另一个函数g
,定义在外部范围内。所以从这个意义上说,f
的返回值取决于 的定义g
。并且该函数g
没有作为参数传递f
- 至少没有明确传递。这不会破坏引用透明度吗?
关键是不变性。
我将通过与默认情况下允许可变性的语言 - Javascript 进行比较来说明这一点。考虑以下代码:
f = function(x) {
return x;
}
g = function(y) {
f = function(x) {
return x+y;
}
}
现在您可以打破引用透明度:
f(1) -- returns 1
g(10)
f(1) -- returns 11
我们没有引用透明度,因为您不能f
用它们的值替换调用。例如,在代码中
console.log(f(1))
g(10)
console.log(f(1))
你可能会想象你可以f(1)
用它们的值(1
)替换这两个调用,得到
console.log(1)
g(10)
console.log(1)
即输出1
到控制台两次的一段代码。但实际上,运行原始代码输出1
然后输出11
,因为介入调用g(10)
.
这在 Haskell 中是不可能的,因为所有值都是不可变的。换句话说,在 Haskell 中,您不能编写g
修改其范围之外的另一个函数的值的函数。在 Haskell 中,您始终可以用它们的值替换函数调用,而不会破坏您的代码。
参照透明意味着
f s = "Hello " ++ g s
g s = s ++ "!"
无法区分
f s = "Hello " ++ h s
where h s = s ++ "!"
g s = s ++ "!"
和
f s = "Hello " ++ s ++ "!"
g s = s ++ "!"
这意味着您可以g
在f
不改变f
.
如果它要改变 的含义f
,你就必须以g
某种方式改变。如何?
如果g
不作为参数传递,那么它是一个“顶级声明”。并且此类声明不能在运行时更改。他们可以改变的唯一方法是重新编译程序。
因此,函数在运行时产生不同结果的唯一方法是其声明的输入发生变化。它“取决于”其他几件事,但这些其他事情都不能在运行时改变。