Are closures a violation of the functional program

2019-03-09 09:50发布

问题:

Functional programming "avoids state and mutable data".

Closures hide state by binding their lexical environment and are thus closed over their free variables.

How is Haskell purely-functional if it supports closures? Don't they break referential transparency?

回答1:

In Haskell, closures have free variables in the same way that in math you can write f x = x^2 - it doesn't mutate state.

I would say that Haskell avoids mutable state.



回答2:

Closures are not a violation because all bindings in Haskell are immutable. What closures really mean is that a lambda with free variables doesn't denote one unique function; it will denote different functions depending on the bindings that are in effect for its free variables when each time it is evaluated. E.g.:

makeClosure :: Num a => a -> a -> a
makeClosure x = \y -> x+y

The expression makeClosure 5 evaluates to a different function than makeClosure 6; and much more importantly, two occurrences of makeClosure 5 in different parts of the program evaluate to the same function, as does makeClosure (2+3) or similar; i.e., we have referential transparency (substituting expressions with their equals preserves the meaning of a program).

You seem to be confused over the meaning of "state" in the quote you mention. State in this context means mutable data; closures can definitely "hide" data, but in Haskell this data is not mutable, so it doesn't hide state. As a contrast to this, in my experience Java programmers often say that a class instance "hides state" in cases where the data in question is not mutable, e.g., assigned to a private final instance field from the constructor; what they really mean is that classes (and closures) encapsulate data.



回答3:

No, closures are fine and don't cause problems in Haskell because a closure closes over the values of the free variables. The reason you can hide state behind closures in other languages is that you close over the reference. As you know, in Javascript:

var x = 1;
var f = function(y) { return y + x; }
f(2)  // => 3
x = 2;
f(2)  // => 4

You can actually model this by using IORefs in Haskell:

main = do
  x <- newIORef 1
  let f y = do x' <- readIORef x
               return (y + x')
  r1 <- f 2
  writeIORef x 2
  r2 <- f 2

This is OK because function f has type Int -> IO Int rather than Int -> Int. In other words, f is bound to the same action, but when executed that same action may return different results each time.



回答4:

My working man's definition of "functional programming" is that if you put the same thing(s) in, you always get the same thing(s) out.

Closures do not violate this definition in Haskell (try and come up with a closure that does :) ), therefore closures don't violate the FP paradigm.