search
Quick tour

Eclipse Golo JVM programming language

Julien Ponge
JHipster 4
Solar eclipse image via Shutterstock

Yet another language? That is a good question to ask, especially when the Java Virtual Machine (JVM) is host to many languages with proven adoption. While Golo is indeed “yet another JVM language”, it has interesting applications for tinkerers as a standalone language, as an embedded language into other JVM applications, or even for quick prototyping in IoT settings.

Eclipse Golo started out of the experiments from the Dynamid research team at CITI-Inria laboratory and INSA-Lyon. As we were investigating the possibilities offered by the new invokedynamic JVM bytecode instruction in Java 7, we ended up designing a simple programming language around that instruction and the corresponding java.lang.invoke API. We thought it would be interesting to pursue the development of Golo for the following reasons:

  1. some other JVM languages port their semantics on top of invokedynamic, while we went the other way by designing the language around it with performance patterns in mind, and
  2. other proven JVM languages tend to have intimidating code bases, especially for students, and we needed something more approachable to experiment with language or runtime research.

While Golo is indeed “yet another JVM language”, it has interesting applications for tinkerers as a standalone language, as an embedded language into other JVM applications, or even for quick prototyping in IoT settings.

Golo is now a Technology Project at the Eclipse Foundation, and a small friendly community is taking care of its evolution.

Getting started with Golo

Golo files usually use the .golo extension. Each Golo file defines a module, as in:

module EchoArgs

function main = |args| {
  println("Here are the command line arguments, if any:")
  foreach arg in args {
    println("-> " + arg)
  }
}

The main function of a module can serve as a program entry-point with command-line arguments being passed as an array. Golo programs can be compiled to JVM bytecode, or they can be executed directly. In each case the sole golo command-line tool provides a specific sub-command:

$ golo compile echo-args.golo
$ golo golo --files echo-args.golo --args 1 2 3 4 5
Here are the command line arguments, if any:
-> 1
-> 2
-> 3
-> 4
-> 5
$

The compile sub-command compiles to JVM bytecode, run executes some already-compiled Golo code, and golo serves as a shortcut to compile and directly run some code. There also exists a support for Unix-style “shebang” scripts:

module EchoArgs

function main = |args| {
  println("Here are the command line arguments, if any:")
  foreach arg in args {
    println("-> " + arg)
  }

By making the .golo file executable, we can directly run it:

$ chmod +x echo-args.golo
$ ./echo-args.golo 1 2 3 4 5
Here are the command line arguments, if any:
-> 1
-> 2
-> 3
-> 4
-> 5
$

Collection literals, anonymous functions and collection comprehensions

Golo provides support for collection literals: tuples, lists, vectors, sets, and maps. It is possible to create a list as follows:

# Note: an instance of java.util.ArrayList
#
# This is equivalent to:
#   let numbers = java.util.ArrayList()
#   numbers: add(1)
#   (...)
let numbers = list[1, 2, 3, 4, 5]

All collection literals are backed by java.util collections bar n-ary tuples that rely on a Golo-specific implementation. This provides great interoperability with Java libraries.

It is possible to invoke methods on objects in Golo using : between a receiver object and a method. Collections have additional methods to support functional idioms, so the following code takes the numbers list above, generates a new list with items incremented by 10 using map, and then computes the sum of all elements using reduce:

let sum = numbers:
  map(|n| -> n + 10):
  reduce(0, |acc, next| -> acc + next)

We can also see how anonymous functions are being passed to the map and reduce methods. It is possible to assign a reference to a function, compose it with another function, and call it by name:

let f = (|n| -> n + 1): andThen(|n| -> n * 2)
println(f(3))   # prints 8

Finally, Golo provides collection comprehensions that are very similar to those from Python:

Here nums is a set of tuples [i, j] where i is an even number between 1 and 100, and j is a character between a and z. This also introduced range notations, as in [1..100].

Defining types

Golo does not support the definition of classes, but it supports structures, unions and dynamic object types.

Dynamic objects

The simplest type definition is DynamicObject:

let p1 = DynamicObject():
  define("name", "Someone"):
  define("email", "someone@some-provider.tld"):
  define("prettyPrint", |this| -> println(this: name() + " <" + this: email() + ">"))

p1: prettyPrint()

The define reserved method allows creating attributes or methods, which can then be used by name. In many ways this construction is similar to the “expando” objects in Groovy. There also exists a fallback method definition that captures all invocations to any method that hasn’t been defined. This can be useful for designing flexible APIs, much like some idioms from the Ruby / Rails communities.

Structures

Structures can be created as follows:

struct Point = {x, y}

This defines a data structure for points, and it can be created and manipulated as follows:

var p2 = Point(1, 2)
println(p2)   # "struct Point{x=1, y=2}"

p2: x(4)
println(p2: x())

let x, y = p2
println(x)
println(y)

Note that Golo supports destructuring, so that x and y get the values from the structure object. This also works for collections, including map entries.

This Point definition does not do much alone, but we can add methods using augmentations. Augmentations can add methods to any type, including existing Java classes. An augmentation applies to code from the same module, or to code from another module that imports its definition. Here is how we can add shift and dump methods to Point objects:

augment Point {

  function shift = |this, dx, dy| ->
    Point(this: x() + dx, this: y() + dy)

  function dump = |this| ->
    println("(" + this: x() + ", " + this: y() + ")")
}

With this augmentation visible we can then use the methods:

p2 = p2: shift(1, 4)
p2: dump()

Enumerations

Enumerations provide an elegant way to define custom types as tagged unions, sometimes also referred to as sum algebraic data types. Here is how one could define binary trees using an empty type, a leaf type and a node type:

union Tree = {
  Empty
  Leaf = { value }
  Node = { left, right }
}

With this definition, we can create both a tree and an empty tree:

let t0 = Tree.Empty()
let t1 = Tree.Node(
  Tree.Leaf(1), Tree.Node(
    Tree.Leaf(2), Tree.Leaf(3) ))

Union types get query methods to check for their types, so that:

println(t0: isEmpty())
println(t1: isNode())

prints true for each test, since t0 is an empty tree and t1 is a leaf. It is also possible to check not just for the type but also for values:

let _ = Unknown.get()
println(t1: isNode(Tree.Leaf(1), _))

Unknown.get() returns a placeholder value for union types, which allows us to check that t1 is a node whose left value is a leaf of value 1, while the right value is being ignored.

Decorators

Golo also supports Python-style function decorators. A decorator is basically a higher-order function, that is, a function that returns a function. Let’s get more practical, and let’s define the trace function below:

function trace = |message| -> |fun| -> |args...| {
  println("Calling " + message)
  println(">>> arguments: " + args: toString())
  let res = fun: invoke(args)
  println(">>> return value: " + res)
  return res
}

That function takes a message, and returns a function that decorates another function and captures its arguments (args...is for variable length arguments, so it can capture any function arity). We can then decorate a function, as in:

@trace("Sum function")
function add = |a, b| -> a + b

When we call add(1, 2), we get the following console output:

Calling Sum function
>>> arguments: [1, 2]
>>> return value: 3

That was only a brief overview!

This article did not highlight all the features of Eclipse Golo, and we encourage you to give it a try and discuss with the community.

We would like to make a special call to other Eclipse projects: ironically Golo does not have a proper Eclipse IDE support yet. The best support remains the Atom or Vim editors. You will be more than welcome if you feel like helping us in bringing first-class support for Eclipse Golo in the Eclipse IDE!

eclipseorb_color

This post was originally published in the October 2016 issue of the Eclipse Newsletter: Discover Eclipse Runtimes

For more information and articles check out the Eclipse Newsletter.

Author

Julien Ponge

Julien Ponge is a computer scientist at CITI Laboratory.


Comments
comments powered by Disqus