Closures in Elm

Elm 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 Html exposing (Html, div, text)

-- 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 : () -> (() -> Int)
intSeq () =
    let
        i = ref 0
    in
    \() ->
        i := !i + 1
        !i

-- 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 : () -> Int
nextInt =
    intSeq ()

-- See the effect of the closure by calling `nextInt`
-- a few times.
main : Html msg
main =
    div []
        [ text (String.fromInt (nextInt ()))
        , text (String.fromInt (nextInt ()))
        , text (String.fromInt (nextInt ()))
        -- To confirm that the state is unique to that
        -- particular function, create and test a new one.
        , text (String.fromInt ((intSeq ()) ()))
        ]

In this Elm code:

  1. We define the intSeq function that returns another function. This inner function forms a closure over the i variable.

  2. We use Elm’s ref to create a mutable reference, which is similar to the mutable variable in the original example.

  3. The nextInt function is created by calling intSeq ().

  4. In the main function, we demonstrate the effect of the closure by calling nextInt () multiple times and displaying the results.

  5. We also create a new closure to show that its state is separate from the first one.

To run this Elm program, you would typically compile it and then open the resulting HTML file in a browser. The output would be displayed on the web page.

The last feature of functions we’ll look at for now is recursion.