3

我正在学习 Haskell。如果我理解正确,Haskell 中的一个简单函数始终是引用透明的。我认为这意味着它的输出仅取决于传递给它的参数。

但是一个函数f可以调用另一个函数g,定义在外部范围内。所以从这个意义上说,f的返回值取决于 的定义g。并且该函数g没有作为参数传递f- 至少没有明确传递。这不会破坏引用透明度吗?

4

3 回答 3

8

关键是不变性

我将通过与默认情况下允许可变性的语言 - 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 中,您始终可以用它们的值替换函数调用,而不会破坏您的代码。

于 2012-10-04T08:23:26.900 回答
6

参照透明意味着

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 ++ "!"

这意味着您可以gf不改变f.

如果它要改变 的含义f,你就必须以g某种方式改变。如何?

于 2012-10-04T08:13:09.363 回答
4

如果g不作为参数传递,那么它是一个“顶级声明”。并且此类声明不能在运行时更改。他们可以改变的唯一方法是重新编译程序。

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

于 2012-10-04T08:26:40.937 回答