Closures in Haskell
Haskell supports anonymous functions, which can form closures. Anonymous functions are useful when you want to define a function inline without having to name it.
import System.IO
-- This function `intSeq` returns another function, which
-- we define anonymously in the body of `intSeq`. The
-- returned function closes over the variable `i` to
-- form a closure.
intSeq :: IO (IO Int)
intSeq = do
ref <- newIORef 0
return $ do
modifyIORef ref (+1)
readIORef ref
main :: IO ()
main = do
-- We call `intSeq`, assigning the result (a function)
-- to `nextInt`. This function value captures its
-- own `ref` value, which will be updated each time
-- we call `nextInt`.
nextInt <- intSeq
-- See the effect of the closure by calling `nextInt`
-- a few times.
print =<< nextInt
print =<< nextInt
print =<< nextInt
-- To confirm that the state is unique to that
-- particular function, create and test a new one.
newInts <- intSeq
print =<< newInts
To run the program:
$ runhaskell closures.hs
1
2
3
1
In this Haskell version, we use IORef
to emulate mutable state within the IO
monad. The intSeq
function returns an IO (IO Int)
, which is a monadic action that, when executed, produces another monadic action that returns an Int
.
The closure is created by the inner anonymous function, which captures the ref
variable and modifies it each time it’s called.
In the main
function, we demonstrate the use of these closures, showing how each instance maintains its own state.
The last feature of functions we’ll look at for now is recursion.