Closures in PureScript

PureScript supports anonymous functions, which can form closures. Anonymous functions are useful when you want to define a function inline without having to name it.

module Main where

import Prelude
import Effect (Effect)
import Effect.Console (log)

-- 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 :: Effect (Effect Int)
intSeq = do
  i <- pure 0
  pure $ Effect do
    i' <- pure (i + 1)
    pure i'

main :: Effect Unit
main = do
  -- We call `intSeq`, assigning the result (a function)
  -- to `nextInt`. This function value captures its
  -- own `i` value, which will be updated each time
  -- we call `nextInt`.
  nextInt <- intSeq

  -- See the effect of the closure by calling `nextInt`
  -- a few times.
  n1 <- nextInt
  log $ show n1
  n2 <- nextInt
  log $ show n2
  n3 <- nextInt
  log $ show n3

  -- To confirm that the state is unique to that
  -- particular function, create and test a new one.
  newInts <- intSeq
  n4 <- newInts
  log $ show n4

To run this PureScript program, you would typically compile it and then run it using Node.js:

$ spago build
$ node -e "require('./output/Main').main()"
1
2
3
1

In PureScript, we use the Effect monad to handle side effects, which is why our intSeq function returns Effect (Effect Int) instead of just () -> Int. The inner Effect represents the side effect of incrementing and returning the counter.

The do notation is used for sequencing effectful computations, which is why we see it used in both intSeq and main.

The last feature of functions we’ll look at for now is recursion, which PureScript also supports.