Functions are first-class citizen in Golo. Here is how to define and call some.
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()
Of course functions may take some parameters, as in:
function addition = |a, b| { return a + b }
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!")
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))
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() }
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 }
Golo modules have a set of implicit imports:
gololang.Predefined
,
gololang.StandardAugmentations
,
gololang
,
java.lang
.
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() }