为什么使用在外部范围中定义的函数不会破坏引用的透明度



我正在学习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没有作为参数传递,那么它就是一个"顶级声明"。而且这样的声明在运行时不能更改。它们可以更改的唯一方法是重新编译程序。

因此,函数在运行时产生不同结果的唯一方法是其声明的输入发生更改。它"取决于"其他一些事情,但这些其他事情在运行时都不会改变。

最新更新