函数式编程“避免状态和可变数据”。
闭包通过绑定其词法环境来隐藏状态,从而关闭其自由变量。
如果Haskell支持闭包,它如何成为纯函数式语言? 它们不会破坏引用透明性吗?
函数式编程“避免状态和可变数据”。
闭包通过绑定其词法环境来隐藏状态,从而关闭其自由变量。
如果Haskell支持闭包,它如何成为纯函数式语言? 它们不会破坏引用透明性吗?
f x = x^2
- 它不会改变状态。闭包并不违反Haskell中的所有绑定都是不可变的这一原则。闭包实际上意味着具有自由变量的lambda表达式并不能表示唯一的函数;每次对其求值时,它都将表示不同的函数,具体取决于其自由变量的绑定情况。例如:
makeClosure :: Num a => a -> a -> a
makeClosure x = \y -> x+y
makeClosure 5
和 makeClosure 6
这两个表达式会评估为不同的函数;更重要的是,程序中不同部分中出现的两个 makeClosure 5
、以及 makeClosure (2+3)
等都会评估为相同的函数。也就是说,我们有引用透明性(用等价的表达式替换原表达式保留程序含义)。在该引述中,“状态”一词的含义可能令你感到困惑。在此上下文中,“状态”指的是可变数据;闭包确实可以“隐藏”数据,但在 Haskell 中,这些数据是不可变的,因此它们并不隐藏状态。相比之下,在我的经验中,Java 程序员经常会在数据不可变的情况下将类实例“隐藏状态”,例如将数据从构造函数赋值给private final
实例字段;他们真正意思的是类(和闭包)封装数据。var x = 1;
var f = function(y) { return y + x; }
f(2) // => 3
x = 2;
f(2) // => 4
实际上,你可以使用 Haskell 中的 IORef
来建模:
main = do
x <- newIORef 1
let f y = do x' <- readIORef x
return (y + x')
r1 <- f 2
writeIORef x 2
r2 <- f 2
这是可以的,因为函数 f
的类型为 Int -> IO Int
而不是 Int -> Int
。换句话说,f
绑定到相同的操作,但是当执行该操作时,每次可能返回不同的结果。
在我这个工作人士的定义中,“函数式编程”的意思是,如果你输入相同的东西,你总是会得到相同的东西。
在Haskell中,闭包不违反这个定义(试着想出一个违反它的闭包吧 :)),因此闭包并不违反函数式编程范例。