我正在学习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)
即两次向控制台输出CCD_ 10的一段代码。但实际上,由于对g(10)
的中间调用,运行原始代码会输出1
,然后输出11
。
这在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
没有作为参数传递,那么它就是一个"顶级声明"。而且这样的声明在运行时不能更改。它们可以更改的唯一方法是重新编译程序。
因此,函数在运行时产生不同结果的唯一方法是其声明的输入发生更改。它"取决于"其他一些事情,但这些其他事情在运行时都不会改变。