6. Closures

Golo supports closures, which means that functions can be treated as first-class citizen.

6.1. Defining and using a closure

Defining a closure is straightforward as it derives from the way a function can be defined:

let adder = |a, b| {
  return a + b
}

At runtime, a closure is an instance of java.lang.invoke.MethodHandle. This means that you can do all the operations that method handles support, such as invoking them or inserting arguments as illustrated in the following example:

let adder = |a, b| {
  return a + b
}
println(adder: invokeWithArguments(1, 2))

let addToTen = adder: bindTo(10)
println(addToTen: invokeWithArguments(2))

As one would expect, this prints 3 and 12.

6.2. Compact closures

Golo supports a compact form of closures for the cases where their body consists of a single expression. The example above can be simplified as:

let adder = |a, b| -> a + b

You may also use this compact form when defining regular functions, as in:

module Foo

local function sayHello = |who| -> "Hello " + who + "!"

# Prints "Hello Julien!"
function main = |args| {
  println(sayHello("Julien"))
}

6.3. Calling closures

While you may take advantage of closures being method handles and call them using invokeWithArguments, there is a (much) better way.

When you have a reference to a closure, you may simply call it as a regular function. The previous adder example can be equivalently rewritten as:

let adder = |a, b| -> a + b
println(adder(1, 2))

let addToTen = adder: bindTo(10)
println(addToTen(2))

6.4. Limitations

Closures have access to the lexical scope of their defining environment. Consider this example:

function plus_3 = {
  let foo = 3
  return |x| -> x + foo
}

The plus_3 function returns a closure that has access to the foo reference, just as you would expect. The foo reference is said to have been captured and made available in the closure.

It is important to note that captured references are constants within the closure. Consider the following example:

var a = 1
let f = {
  a = 2   # Compilation error!
}

The compilation fails because although a is declared using var in its original scope, it is actually passed as an argument to the f closure. Because function parameters are implicitly constant references, this results in a compilation error.

That being said, a closure has a reference on the same object as its defining environment, so a mutable object is a sensible way to pass data back from a closure as a side-effect, as in:

let list = java.util.LinkedList()
let pump_it = {
  list: add("I heard you say")
  list: add("Hey!")
  list: add("Hey!")
}
pump_it()
println(list)

which prints [I heard you say, Hey!, Hey!].

6.5. Closures to single-method interfaces

The Java SE APIs have plenty of interfaces with a single method: java.util.concurrent.Callable, java.lang.Runnable, javax.swing.ActionListener, etc.

The predefined function asInterfaceInstance can be used to convert a method handle or Golo closure to an instance of a specific interface.

Here is how one could pass an action listener to a javax.swing.JButton:

let button = JButton("Click me!")
let handler = |event| -> println("Clicked!")
button: addActionListener(asInterfaceInstance(ActionListener.class, handler))

Because the asInterfaceInstance call consumes some readability budget, you may refactor it with a local function as in:

local function listener = |handler| -> asInterfaceInstance(ActionListener.class, handler)

# (...)
let button = JButton("Click me!")
button: addActionListener(listener(|event| -> println("Clicked!")))

Here is another example that uses the java.util.concurrent APIs to obtain an executor, pass it a task, fetch the result with a Future object then shut it down:

function give_me_hey = {
  let executor = Executors.newSingleThreadExecutor()
  let future = executor: submit(asInterfaceInstance(Callable.class, -> "hey!"))
  let result = future: get()
  executor: shutdown()
  return result
}

6.6. Conversion to single-method interfaces

Instead of using asInterfaceInstance, you may use a class augmentation which is described later in this documentation. In short, it allows you to call a to method on instances of MethodHandle, which in turn calls asInterfaceInstance. Back to the previous examples, the next 2 lines are equivalent:

# Calling asInterfaceInstance
future = executor: submit(asInterfaceInstance(Callable.class, -> "hey!"))

# Using a class augmentation
future = executor: submit((-> "hey!"): to(Callable.class))

6.7. Getting a reference to a closure / Golo function

You may also take advantage of the predefined fun function to obtain a reference to a closure, as in:

import golotest.Closures

local function local_fun = |x| -> x + 1

function call_local_fun = {

  # local_fun, with a parameter
  var f = fun("local_fun", golotest.Closures.module, 1)

  # ...or just like this if there is only 1 local_fun definition
  f = fun("local_fun", golotest.Closures.module)

  return f(1)
}

Last but not least, we have an even shorter notation if function are not overridden:

import golotest.Closures

local function local_fun = |x| -> x + 1

function call_local_fun = {

  # In the current module
  var f = ^fun

  # ...or with a full module name
  f = ^golotest.Closures::fun

  return f(1)
}

6.8. Binding and composing

Because closure references are just instances of java.lang.invoke.MethodHandle, you can bind its first argument using the bindTo(value) method. If you need to bind an argument at another position than 0, you may take advantage of the bindAt(position, value) augmentation:

let diff = |a, b| -> a - b
let minus10 = diff: bindAt(1, 10)

# 10
println(minus10(20))

You may compose function using the andThen augmentation method:

let f = (|x| -> x + 1): andThen(|x| -> x - 10): andThen(|x| -> x * 100)

# -500
println(f(4))

6.9. Calling functions that return functions

Given that functions are first-class objects in Golo, you may define functions (or closures) that return functions, as in:

let f = |x| -> |y| -> |z| -> -> x + y + z

You could use intermediate references to use the f function above:

let f1 = f(1)
let f2 = f1(2)
let f3 = f2(3)

# Prints '6'
println(f3())

Golo supports a nicer syntax if you don’t need intermediate references:

# Prints '6'
println(f(1)(2)(3)())

Important

This syntax only works following a function or method invocation, not on expressions. This means that:

foo: bar()("baz")

is valid, while:

(foo: bar())("baz")

is not. Let us say that "It is not a bug, it is a feature".