Let us start with the Golo basics.
Editor and IDE support for Golo is available for:
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.
Newlines are important in Golo, so make sure that your editor ends files with a newline.
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.
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 $
Golo provides a golo
command for running compiled Golo code:
$ cd classes $ golo run --module hello.World Hello world! $
Simple, isn’t it?
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
Golo comments start with a #
, just like in Bash, Python or Ruby:
# This is a comment println("WTF?") # it works here, too
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
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 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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?
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 |
|
|
Array |
|
|
List |
|
|
Vector |
|
|
Set |
|
|
Map |
|
|
Tuples essentially behave as immutable arrays.
The gololang.Tuple
class provides the following methods:
get(index)
method to get the element at a specified index,
size()
and isEmpty()
methods that do what their names suggest,
iterator()
method because tuples are iterable, and
equals(other)
, hashCode()
and toString()
do just what you would expect.
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:
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()]
Golo supports the following set of operators.
Symbol(s) | Description | Examples |
---|---|---|
| Addition on numbers and strings. |
|
| Subtraction on numbers. |
|
| Multiplication on numbers and strings. |
|
| Division on numbers. |
|
% | Modulo on numbers. |
|
| Comparison between numbers and objects that implement |
|
| Comparison of reference equality. |
|
| Boolean operators. |
|
| Checks the type of an object instance, equivalent to the |
|
| Evaluates an expression and returns the value of another one if |
|
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())
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()
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"]