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?
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.
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.
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 IORef
s 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.
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.