1. Basics

Let us start with the Golo basics.

1.1. Editor / IDE support

Editor and IDE support for Golo is available for:

1.2. Hello world

Golo source code need to be placed in modules. Module names are separated with dots, as in:

Foo
foo.Bar
foo.bar.Baz
(...)

It is suggested yet not enforced that the first elements in a module name are in lowercase, and that the last one have an uppercase first letter.

A Golo module can be executable if it has a function named main and that takes an argument for the JVM program arguments:

module hello.World

function main = |args| {
  println("Hello world!")
}

println is a predefined function that outputs a value to the standard console. As you can easily guess, here we output Hello, world! and that is an awesome achievement.

Note

Newlines are important in Golo, so make sure that your editor ends files with a newline.

1.3. Running "Hello world"

Of course, we need to run this incredibly complex application.

Golo comes with a golo script found in the distribution bin/ folder. It provides several commands, notably:

  • version to query the Golo version,
  • compile to compile some Golo code to JVM classes,
  • run to execute some already compiled Golo code, and
  • golo to directly execute Golo code from source files, and
  • diagnose to print compiler internal diagnosis information.

The complete commands usage instructions can be listed by running golo --help.

Provided that golo is available from your current $PATH, you may run the program above as follows:

$ golo golo --files samples/helloworld.golo
Hello world!
$

golo golo takes several Golo source files as input. It expects the last one to have a main function to call. The Golo code is compiled on the fly and executed straight into a JVM.

You may also pass arguments to the main function by appending --args on the command line invocation. Suppose that we have a module EchoArgs as follows:

module EchoArgs

function main = |args| {
  foreach arg in args {
    println("->  " + arg)
  }
}

We may invoke it as follows:

$ golo golo --files samples/echo-args.golo --args plop da plop
-> plop
-> da
-> plop
$

Note that args is expected to be an array.

Finally, the --classpath flag allows to specify a list of classpath elements, which can be either directories or .jar files. See the golo help command for details on the various Golo commands.

1.4. Compiling Golo source code

Golo comes with a compiler that generates JVM bytecode in .class files. We will give more details in the chapter on interoperability with Java.

Compiling Golo files is straightforward:

$ golo compile --output classes samples/helloworld.golo
$

This compiles the code found in samples/helloworld.golo and outputs the generated classes to a classes folder (it will be created if needed):

$ tree classes/
classes/
└── hello
    └── World.class

1 directory, 1 file
$

1.5. Running compiled Golo code

Golo provides a golo command for running compiled Golo code:

$ cd classes
$ golo run --module hello.World
Hello world!
$

Simple, isn’t it?

1.6. Passing JVM-specific flags

Both golo and run commands can be given JVM-specific flags using the JAVA_OPTS environment variable.

As an example, the following runs fibonacci.golo and prints JIT compilation along the way:

# Exporting an environment variable
$ export JAVA_OPTS=-XX:+PrintCompilation
$ golo golo --files samples/fibonacci.golo

# ...or you may use this one-liner
$ JAVA_OPTS=-XX:+PrintCompilation golo golo --files samples/fibonacci.golo

1.7. Comments

Golo comments start with a #, just like in Bash, Python or Ruby:

# This is a comment
println("WTF?") # it works here, too

1.8. Variable and constant references

Golo does not check for types at compile time, and they are not declared. Everything happens at runtime in Golo.

Variables are declared using the var keyword, while constant references are declared with let. It is strongly advised that you favour let over var unless you are certain that you need mutability.

Variables and constants need to be initialized when declared. Failing to do so results in a compilation error.

Here are a few examples:

# Ok
var i = 3
i = i + 1

# The assignment fails because truth is a constant
let truth = 42
truth = 666

# Invalid statement, variables / constants have to be initialized
var foo

Valid names contain upper and lower case letters within the [a..z] range, underscores (_), dollar symbols ($) and numbers. In any case, an identifier must not start with a number.

# Ok, but not necessarily great for humans...
let _$_f_o_$$666 = 666

# Wrong!
let 666_club = 666

1.9. Data literals

Golo supports a set of data literals. They directly map to their counterparts from the Java Standard API. We give them along with examples in the data literals table below.

Java type Golo literals

null

null

java.lang.Boolean

true or false

java.lang.String

"hello world"

java.lang.Character

'a', 'b', …

java.lang.Integer

123, -123, 1_234, …

java.lang.Long

123_L, -123_L, 1_234_L, …

java.lang.Double

1.234, -1.234, 1.234e9, …

java.lang.Float

1.234_F, -1.234_F, 1.234e9_F, …

java.lang.Class

String.class, java.lang.String.class, gololang.Predef.module, …

java.lang.invoke.MethodHandle

^foo, ^some.module::foo, …

Speaking of strings, Golo also supports multi-line strings using the """ delimiters, as in:

let text = """This is
a multi-line string.
  How
    cool
      is
        that?"""

println(text)

This snippet would print the following to the standard console output:

This is
a multi-line string.
  How
    cool
      is
        that?

1.10. Collection literals

Golo support special support for common collections. The syntax uses brackets prefixed by a collection name, as in:

let s = set[1, 2, "a", "b"]
let v = vector[1, 2, 3]
let m = map[[1, "a"], [2, "b"]]
# (...)

The syntax and type matchings are the following:

Collection Java type Syntax

Tuple

gololang.Tuple

tuple[1, 2, 3], or simply [1, 2, 3]

Array

java.lang.Object[]

array[1, 2, 3]

List

java.util.LinkedList

list[1, 2, 3]

Vector

java.util.ArrayList

vector[1, 2, 3]

Set

java.util.LinkedHashSet

set[1, 2, 3]

Map

java.util.LinkedHashMap

map[[1, "a"], [2, "b"]]

A note on tuples

Tuples essentially behave as immutable arrays.

The gololang.Tuple class provides the following methods:

  • a constructor with a variable-arguments list of values,
  • a get(index) method to get the element at a specified index,
  • size() and isEmpty() methods that do what their names suggest,
  • an iterator() method because tuples are iterable, and
  • equals(other), hashCode() and toString() do just what you would expect.

A note on maps

The map collection literal expects entries to be specified as tuples where the first entry is the key, and the second entry is the value. This allows nested structures to be specified as in:

map[
  ["foo", "bar"],
  ["plop", set[1, 2, 3, 4, 5]],
  ["mrbean", map[
    ["name", "Mr Bean"],
    ["email", "bean@outlook.com"]
  ]]
]

There are a few rules to observe:

  • not providing a series of tuples will yield class cast exceptions,
  • tuples must have at least 2 entries or will yield index bound exceptions,
  • tuples with more than 2 entries are ok, but only the first 2 entries matter.

Because of that, the following code compiles but raises exceptions at runtime:

let m1 = map[1, 2, 4, 5]
let m2 = map[
  [1],
  ["a", "b"]
]

The rationale for map literals to be loose is that we let you put any valid Golo expression, like functions returning valid tuples:

let a = -> [1, 'a']
let b = -> [2, 'b']
let m = map[a(), b()]

1.11. Operators

Golo supports the following set of operators.

Symbol(s) Description Examples

+

Addition on numbers and strings.

1 + 2 gives 3.

"foo" + "bar" gives "foobar".

"foo" + something where something is any object instance is equivalent to "foo" + something.toString() in Java.

-

Subtraction on numbers.

4 - 1 gives 3.

*

Multiplication on numbers and strings.

2 * 2 gives 4.

"a" * 3 gives "aaa".

/

Division on numbers.

4 / 2 gives 2.

%

Modulo on numbers.

4 % 2 gives 0, 3 % 2 gives 1.

"<", "<=", "==", "!=", ">", ">="

Comparison between numbers and objects that implement java.lang.Comparable. == is equivalent to calling Object#equals(Object) in Java.

1 < 2 gives true.

is, isnt

Comparison of reference equality.

a is b gives true only if a and b reference the same object instance.

and, or, not

Boolean operators. not is of course a unary operator.

true and true gives true, not(true) gives false.

oftype

Checks the type of an object instance, equivalent to the instanceof operator in Java.

("plop" oftype String.class) gives true.

orIfNull

Evaluates an expression and returns the value of another one if null.

null orIfNull "a" gives "a". foo() orIfNull 0 gives the value of calling foo(), or 0 if foo() returns null.

1.12. Calling a method

Although we will discuss this in more details later on, you should already know that : is used to invoke instance methods.

You could for instance call the toString() method that any Java object has, and print it out as follows:

println(123: toString())
println(someObject: toString())

1.13. Java / JVM arrays

As you probably know, arrays on the JVM are special objects. Golo deals with such arrays as being instances of Object[] and does not provide a wrapper class like many languages do. A Java / JVM array is just what it is supposed to be.

Golo adds some sugar to relieve the pain of working with arrays. Golo allows some special methods to be invoked on arrays:

  • get(index) returns the value at index,
  • set(index, value) sets value at index,
  • length() and size() return the array length,
  • iterator() returns a java.util.Iterator,
  • toString() delegates to java.util.Arrays.toString(Object[]),
  • asList() delegates to java.util.Arrays.asList(Object[]),
  • equals(someArray) delegates to java.util.Arrays.equals(this, someArray),
  • getClass() return the array class.

Given a reference a on some array:

# Gets the element at index 0
a: get(0)

# Replaces the element at index 1 with "a"
a: set(1, "a")

# Nice print
println(a: toString())

# Convert to a real collection
let list = a: asList()

Warning

The methods above do not perform array bound checks.

Finally, arrays can be created with the Array function, as in:

let a = Array(1, 2, 3, 4)
let b = Array("a", "b")

You can of course take advantage of the array collection literal, too:

let a = array[1, 2, 3, 4]
let b = array["a", "b"]