Advanced notions

Closures

Functions can be defined inside other functions. Such nested functions have access to their enclosing scope(s): as a result, they can capture variables in their environment and keep a reference to them, even if they go out of scope. Such functions are called closures. Consider the following example:

function outer(c)
   var b = 1

   function inner()

         var a = 2
         print(a + b + c)
    end

   # Return a closure which captures b and c
   return inner
end

# Create two closures
var f1 = outer(3)
var f2 = outer(5)

The function inner() is defined inside outer(), and has access to outer’s variables (its argument c and its local variable b). The variables f1 and f2 instances of the function inner, but each of them with a different captured environment. In f1, the value of c is 3, whereas it is 5 in f2. Since f1 and f2 are functions, we can call them like any regular function:

f1() # prints "6"
f2() # prints "8"

Closures are a powerful construct which allows us to create functions with internal state. They are commonly used to create generators, i.e. functions which can generate new values every time they are called, depending on their internal state. Here is an example of a closure which generates the Fibonacci sequence. We use it to print the first ten values in the sequence.

function fibonacci()
    var first = 0
    var second = 0

    function fib()
        if first == 0 then
            first = 1
            second = 1
            return 0
        else
            var current = first
            var tmp = second
            second = first + second
            first = tmp

            return current
        end
    end

    return fib
end

var f = fibonacci()

for var i = 1 to 10 do
    print(f())
end