为什么使用外部作用域中定义的函数不会破坏参照透明度?

时间:2012-10-04 07:47:37

标签: haskell referential-transparency

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

但是函数f可以调用在外部作用域中定义的另一个函数g。所以在这个意义上,f的返回值取决于g的定义。并且函数g不会作为参数传递给f - 至少不是明确的。这不会破坏参考透明度吗?

3 个答案:

答案 0 :(得分: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中,您始终可以使用它们的值替换函数调用,而不会破坏您的代码。

答案 1 :(得分: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 ++ "!"

这意味着您可以将g内联到f,而不会改变f的含义。

如果要改变f的含义,你必须以某种方式改变g。怎么样?

答案 2 :(得分:4)

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

因此,函数在运行时产生不同结果的唯一方法是它的声明输入是否发生变化。它“依赖于”其他几个东西,但这些其他东西都不会在运行时发生变化。