Functions and Closures - mfichman/jogo GitHub Wiki

Functions

Functions can divide complex tasks into many smaller tasks. They have a name, return type, and parameters:

fibonacci(n Int) Int {
    if n > 2 {
        ret fibonacci(n-1) + fibonacci(n-2)
    } else {
        ret n
    }
}

This is the recursive definition of the Fibonacci sequence. It takes one Int parameter and returns an Int parameter. Note: Jogo uses the ret keyword rather than return, as a nod to x86. This function also happens to be recursive, and is thus tail-call optimized. Here is another function that takes no value and returns no value:

print_hello() {
    print("Hello")
}

Functions in Jogo are first-class objects. This means that they can be stored in variables and passed around. For example:

a = print_hello
a()

or, equivalently,

a = func() {
    print("hello")
}
a()

Note that func is a special keyword that is used to define a function literal, that is, an anonymous function. Functions can also be passed to other functions as arguments:

sort(array Array[Int], compare Function[Int,Int,Bool]) {
    less = Array[Int]()
    greater = Array[Int]()
    if array.length <= 1 {
        ret array
    }
    pivot = array[0]
    for item in array {
        if compare(item, pivot) {
            less.append(item)
        } else {
            greater.append(item)
        }
    }
    ret sort(less) + [item] + sort(greater)
}

This is a (very inefficient) implementation of a quicksort, using compare as the criterion for sorting the array. Above, compare takes two arguments of type Int and returns a Bool. One might call this function like so:

sort(["apple", "pear", "banana"], func(a Int, b Int) Bool {
    ret a < b
})

Closures

When a function is defined, it can use variables that are visible from within the function. For example, the following code creates a function that prints all the values of array starting at i:

array = ["one", "two", "three", "four"]
i = 1
iterate() String {
    if i < array.length {
        print(array[i])
    }
    i = i + 1
}

Thus, iterate is a special kind of function, called a closure, because it "closes over" i and array. Calling iterate() repeatedly will yield each value in the array in turn (notice that # defines the start of a comment):

iterate()   # prints "two"
iterate()   # prints "three"
iterate()   # prints "four"
...

However, if i were defined within iterate, like so:

iterate() String {
    i Int
    if i < array.length {
        print(array[i])
    }
    i = i + 1
}

then the behavior would be quite different, because the i inside iterate now shadows the i outside iterate. In fact, it is a completely different variable. The resulting output would be:

iterate()   # prints "one"
iterate()   # prints "two" 
...

Note also that there is a difference between value-type variables and reference-type variables when it comes to closures. Value-type variables are always passed by value (copied) and so when a closure is created, the variable i above is copied. Reference-type variables, such as array above, are passed by reference, and so array within iterate is an alias to the array object. Thus, another function may modify the contents of array from outside of iterate.