2. Functions

Functions are first-class citizen in Golo. Here is how to define and call some.

2.1. Parameter-less functions

Golo modules can define functions as follows:

module sample

function hello = {
  return "Hello!"
}

In turn, you may invoke a function with a familiar notation:

let str = hello()

A function needs to return a value using the return keyword. Some languages state that the last statement is the return value, but Golo does not follow that trend. We believe that return is more explicit, and that a few keystrokes in favour of readability is still a good deal.

Still, you may omit return statements if your function does not return a value:

function printer = {
  println("Hey!")
}

If you do so, the function will actually return null, hence result in the next statement is null:

# result will be null
let result = printer()

2.2. Functions with parameters

Of course functions may take some parameters, as in:

function addition = |a, b| {
  return a + b
}

Note

Parameters are constant references, hence they cannot be reassigned.

Invoking functions that take parameters is straightforward, too:

let three = addition(1, 2)
let hello_world = addition("hello ", "world!")

2.3. Variable-arity functions

Functions may take a varying number of parameters. To define one, just add ... to the last parameter name:

function foo = |a, b, c...| {
  # ...
}

Here, c catches the variable arguments in an array, just like it would be the case with Java. You can thus treat c as being a Java object of type Object[].

Calling variable-arity functions does not requiring wrapping the last arguments in an array. While invoking the foo function above, the following examples are legit:

# a=1, b=2, c=[]
foo(1, 2)

# a=1, b=2, c=[3]
foo(1, 2, 3)

# a=1, b=2, c=[3,4]
foo(1, 2, 3, 4)

Because the parameter that catches the last arguments is an array, you may call array methods. Given:

function elementAt = |index, args...| {
  return args: get(index)
}

then:

# prints "2"
println(elementAt(1, 1, 2, 3))

2.4. Functions from other modules and imports

Suppose that we have a module Foo.Bar:

module Foo.Bar

function f = {
  return "f()"
}

We can invoke f from another module by prefixing it with its module name:

let r = Foo.Bar.f()

Of course, we may also take advantage of an import statement:

module Somewhere.Else

import Foo.Bar

function plop = {
  return f()
}

Note

Imports in Golo do not work as in Java. Golo is a dynamic language where symbols are being resolved at runtime. Module imports are not checked at compilation time, and their sole purpose is to help in dynamic resolution. Back to the previous example, f cannot be resolved from the current module, and the Golo runtime subsequently tries to resolve f from each import statement. Also, note that the order of import statements is important, as the resolution stops at the first module having the f function.

Last but not least, you may prepend the last piece of the module name. The following invocations are equivalent:

module Somewhere.Else

import Foo.Bar

function plop = {
  let result = f()
  let result_bis = Bar.f()
  let result_full = Foo.Bar.f()
  return result
}

2.5. Local functions

By default, functions are visible outside of their module. You may restrict the visibility of a function by using the local keyword:

module Foo

local function a = {
  return 666
}

function b = {
  return a()
}

Here, b is visible while a can only be invoked from within the Foo module. Given another module called Bogus, the following would fail at runtime:

module Bogus

function i_will_crash = {
  return Foo.a()
}