Function References

We can get a reference to any function the same way as for a variable by using the ampersand operator:

fn incr(nbr u32) u32
  nbr + 1

fn doit()
  imm fnref = &incr  // fn ref points to the function "incr"

A function reference may be copied/passed around like other values. Although it is a borrowed reference, it has no lifetime constraints, since it points to a global entity. Its permission is id, since you may not read or change the function it points to.

The one thing you can do with a function reference (besides copy it or compare it to another reference) is use it to call the function it refers to.

fn doit()
	dostuff(&incr)   // Pass along a reference to the incr function

// Note the parameter needs to declare the reference's function signature
fn dostuff(fnref &fn(u32) u32)
  fnref(4)         // Call the function

Function references are useful when you want the flexibility of embedding some logic that conforms to a certain API within another piece of logic. Thus, it is possible for some other function to call dostuff passing along a reference to a different function (e.g., "double").

Anonymous Functions

When the function you want to get a reference to is small, it may be more convenient to incorporate the logic for the function right where the function reference is to be captured. This can be accomplished using an anonymous function:

fn doit()
	dostuff(&fn(nbr u32) u32 {nbr+1})
	dostuff(&fn(nbr u32) u32 {nbr+nbr})


A closure can be thought of as a function that has a state that is preserved from one execution to the next. The closure's logic may retrieve or alter its state using bound closure variables.

Because closures preserve state information, they are useful for:

A closure can be created almost as easily as an anonymous function. The crucial difference is the need to specify the bound closure variables in curly braces just before the parameter list. For example, here we create a simple generator closure:

imm counter = &fn {mut cnt=0}() {cnt += 1}
counter()   // 1
counter()   // 2

Cone's closures are somewhat different than closures in other languages. Cone expects the bound closure variables to be declared, which often improves code readability. Also, closure variables are bound by value, which is more flexible and reduces the likelihood of errors. Closure variables may hold references, if desired.

Closure variable declarations look like those for any variable and are separated by semicolons. Each variable should always specify an initial value. If the name of the closure variable matches the name of a variable in the outer scope holding the desired value, one can specify "name" as a shortcut for "name=name".

Closures are created as references. As such they can be created as lifetime-constrained borrowed references or using allocators. Likewise, reference permissions may be specified (though the default 'uni' permission is very useful for moving around ownership of event handlers).

Closure Interface

Let's examine a more complex example: using a closure as an event handler.

window.whenClicked(&fn {window}() { ... })

Since our anonymous closure has no named type, what declared type does the whenClicked method expect? It needs to be a type that can accept a number of different anonymous closures. In this case, the correct answer is to use the standard library's predefined Closure interface. Closure is declared like this:

interface Closure
  fn `()`()

With this definition, Closure is a match for any struct that implements the `()` method with no parameters. Since closures are implemented using structs (more on that below), the Closure interface will then match on any closure whose method has no parameters and returns no values.

We can now declare window.whenClicked as follows:

fn whenClicked(handler &Closure) { .. }

When the event fires on a click, the event handler can now call the closure to perform the desired action, which has access to needed state information:


Closures as structs

Cone's anonymous closure syntax is actually syntactic sugar which (under the covers) builds an anonymous struct and then creates an instance of it. The closure's bound variables become the properties of the struct. The defined method becomes the struct's named `()` method, allowing the struct to be "called" as if it were a function.

Here is the equivalent code generated for the earlier example:

struct Handler
	window Window
	fn `()`() { .. }

imm handler = Handler{window}