Golo is a simple dynamic programming language for the Java Virtual Machine that has been designed since the beginning (Java 7) to leverage the capabilities of the Java invokedynamic instruction and API (JSR 292).
Author: Philippe Charrière
Golo has its language constructs designed with invokedynamic in mind. Golo is an exciting language for rapid prototyping, polyglot application embedding, research (e.g. runtime extensions, language prototyping) and teaching (e.g. programming, dynamic language runtime implementation).
Golo in a nutshell
You can see Golo as a kind of sandbox to learn and try many things such as DSL creation, functional programming, IoT, etcetera. Golo is easy to hack; you can modify Golo or create your language from Golo, even if you are not a great Java developer.
– Golo is a research project created by Julien Ponge [1] at CitiLab (INSA Lyon).
– The new tech-lead of the project is Yannick Loiseau [2], part of the Java user group crew of Clermont-Ferrand (France)
– The author, Philippe Charrière [3]was an early adopter (and kind of evangelist) of Golo, and became a core committer of the project.
– Eclipse Golo is a mature Eclipse Technology Project [4].
{ Installation and introduction}
It’s always difficult to present a programming language with few words. For the author, it’s frustrating because you want to talk about everything. To understand a new language quickly the best and shortest way is to provide expressive samples. So let’s start with the mandatory “👋 Hello World 🌍”.
The simplest way to install Golo is to download the latest release (3.3.0)[5] and:
– Unzip golo-3.3.0.zip
– And add the lines below to your path:
export GOLO_HOME='/path/to/golo-distribution/golo' export PATH=$PATH:$GOLO_HOME/bin
A sophisticated “hello world”
module helloWorld struct Person = { name } function main = |args| { let bob = ImmutablePerson("Bob") let message = |person| -> "👋 Hello World 🌍\nI'm " + bob: name() println(message(bob)) }
The output will be:
👋 Hello World 🌍
I’m Bob
If you want to test all the examples for real, you can use GitPod by using this url [6], then navigate to the appropriate directory to run the code:
cd 01-hello-world golo golo --files hello-world.golo
There are several ways to run a Golo program (you can even compile it to a class or jar file). You can find more details on the golo lang documentation[7].
{ Golo is functional }
Functions are the first-class citizens in Golo. But Golo comes with other functional powers.
No iteration
Suppose you are a “serious functional programmer”, you have to try to avoid iteration. It’s pretty easy with Golo because lists have the necessary methods like map, filter, reduce, each … But, let’s create my favourite sandwich.
In the source code below, I want to “prepare a kebab” from an ingredients list (ingredients). You have 3 closures (noPeppers, noOnions, noFries) that you will use as predicates with the filter method of the ingredients list. Every time you call filter you obtain a new list. Then, you will use a fourth closure (pick) to get a new list from a transformation of every ingredient by calling the map method of the last list. And finally, you will transform the new list of ingredients to a string by calling the reduce method with the fifth closure mixIngredients as parameter.
By the way, you can see that you can chain the list methods. In Golo, the ‘:’-notation is for calling the instance methods while the ‘.’-notation is for class methods.
module kebab # How to run it: # golo golo --files kebab.golo function main = |args| { # these closures return a boolean value let noPeppers = |item| -> item isnt "🌶️" let noOnions = |item| -> item isnt "🧅" let noFries = |item| -> item isnt "🍟" # these closure return a string value let pick = |item| -> "piece of " + item let ingredients = list["🍅", "🧅", "🥗", "🍖", "🌶", "🍟"] # I can compose my own kebab: I don't want fries, onions and peppers let myFavouriteRecipe = ingredients # ["🍅", "🧅", "🥗", "🍖", "🌶", "🍟"] : filter(noFries) # ["🍅", "🧅", "🥗", "🍖", "🌶"] : filter(noOnions) # ["🍅", "🥗", "🍖", "🌶"] : filter(noPeppers) # ["🍅", "🥗", "🍖"] : map(pick) # ["piece of 🍅", "piece of 🥗", "piece of 🍖"] println(myFavouriteRecipe) # Deliver the kebab let mixIngredients = |accItem, nextItem| -> accItem + nextItem + " " let kebab = myFavouriteRecipe: reduce("🥙 with ", mixIngredients) println(kebab) # 🥙 with piece of 🍅 piece of 🥗 piece of 🍖 piece of 🌶 }
Handling Errors
A functional programming language without lovely types dedicated to errors handling is not a functional programming language. Golo provides the appropriate functional types for that, as well as some useful helpers. For example, if you decorate a function with the ‘@option’ decorator, the function will return an Optional:
import gololang.Errors @option function animal = |name| { let animals = map[ ["panda", "🐼"], ["tiger", "🐯"], ["bear", "🐻"], ["lion", "🦁"] ] return animals: get(name) }
And you can use the result of the function with a map or an either method:
let panda = animal("panda") : map(|emoji| -> emoji) : orElseGet(-> "😡") animal("cow"): either( default= -> println("😡"), mapping= |value| -> println(value) )
Golo provides a similar type: Result. This “keeps” the errors (it encapsulates the type of error in the form of a Throwable instance that can be raised later).
@result function toInt = |value| { return java.lang.Integer.parseInt(value) }
And you can use it like this, for example:
toInt("42"): either( recover= |error| -> println(error), mapping= |value| -> println(value) )
Or like that:
let error, value = toInt("fourty-two") if (error oftype NumberFormatException.class) { println(error) } else { println(value) }
Read the documentation for more methods and details about error handling [8].
We even provide Union Types
The Union types landed in Golo very early (around 2015). Define a Union is very easy:
union HttpStatus = { Informational = { value } Successful = { value } Redirect = { value } ClientError = { value } ServerError = { value } }
The Union comes with special methods to test for the exact type of a value. And this allows to write readable tests:
let responseStatus = HttpStatus.Successful(201) let check_status_code = |status| -> match { when status: isClientError() then "client error: " + status: value() when status: isServerError() then "server error: " + status: value() when status: isRedirect() then "redirection: " + status: value() when status: isSuccessful() then "successful: " + status: value() when status: isInformational() then "informational: " + status: value() otherwise "🤔" } println(check_status_code(responseStatus))
Error handling and functional programming have never been so pleasant.
{ Golo Loves Java }
Another very convenient quality of Golo is its capacity to use and augment existing Java classes. Then, the field of actions becomes very large, allowing you to define your DSL to simplify complex use cases drastically. I’m using this ‘superpower ‘ with my internet of things experiments. I want to embed an MQTT broker on Raspberry PI Zero, but I need something easily and quickly updatable and improvable. Let see how you can do that:
– For this example, you’ll build a jar file containing a Vert.x distribution with the MQTT helpers.
– All the source code is available here [9]
Create a Golo module
You’ll first create a Golo module, and embed properties (fields) and method augmentations in this module. Module-level references (mqttClients and mqttSubscriptions) are only visible from their module, although a function may provide accessors to them. You can see this module like a mix of class, interface and traits.
module mqttModule # store connected clients and subscriptions let mqttClients = map[] let mqttSubscriptions = map[]
Then, you’ll add an improved get method to java.util.Map to get an Optional when you look for an element in this map and simplify my nulls management:
augment java.util.Map { @option function getOptional = |this, key| -> this: get(key) }
Remark: an augmentation function takes the receiver object as its first argument, followed by optional arguments. The main objective is to simplify the management of the clients and subscriptions. For that, you’ll add four methods to the MqttServer object:
– updateClients
– updateSubscriptions
– mqttClients (accessor to the mqttClients)
– mqttSubscriptions (accessor to the mqttSubscriptions)
augment io.vertx.mqtt.MqttServer { function updateClients = |this, endpoint| { endpoint: accept(false) # update clients connection mqttClients: put(endpoint: clientIdentifier(), endpoint) println("🤖 connected client: " + endpoint: clientIdentifier()) } function updateSubscriptions = |this, clientIdentifier, subscriptionRequest| { # update clients subscriptions subscriptionRequest: topicSubscriptions(): each(|subscription| { mqttSubscriptions: put( clientIdentifier + "-" + subscription: topicName(), subscription ) }) println("😊 new subscriptions(s): " + subscriptionRequest: topicSubscriptions(): head()) } function mqttClients = |this| -> mqttClients function mqttSubscriptions = |this| -> mqttSubscriptions }
And finally, since the needs for publishing messages between connected objects are very simple (in the current context), you’ll add to the MqttEndpoint a simplePublish method to encompass and call the “real” publish method with default parameters:
augment io.vertx.mqtt.MqttEndpoint { function simplePublish = |this, topic, payload| { this: publish( topic, Buffer.buffer(payload: toString()), MqttQoS.AT_LEAST_ONCE(), false, false ) } }
{ Now, let’s use your new Golo module }
From now on, you are able to write MQTT brokers very quickly to embed them on a PI Zero to make gateways on demand easily. This is the main code:
import mqttModule function main = |args| { let vertx = Vertx.vertx() let mqtt_options = MqttServerOptions(): port(1883) let mqtt_server = MqttServer.create(vertx, mqtt_options) mqtt_server: endpointHandler(|endpoint| { mqtt_server: updateClients(endpoint) endpoint: subscribeHandler(|subscriptionRequest| { mqtt_server: updateSubscriptions(endpoint: clientIdentifier(), subscriptionRequest) }) endpoint: publishHandler(|message| { # at every message mqtt_server: mqttClients(): each(|identifier, client| { # check for each client if a subscription exists mqtt_server: mqttSubscriptions(): getOptional(identifier + "-" + message: topicName()) : either( default= -> println("🖐️ no subscription for this client"), mapping= |subscription| -> # send message to the subscribed client client: simplePublish( message: topicName(), message: payload() ) ) }) }) }) mqtt_server: listen() }
Now, you can adapt the broker for all your incoming use cases 🎉.
Remark: To run the code, use this command: golo golo –classpath libs/*.jar –files mqtt.module.golo main.golo
{ Modify Golo }
Another specificity of Golo is that you can, with great ease, develop the language itself and add features and keywords and then recompile it to have an ‘improved’ version of Golo tailored for you (it’s also an excellent way to contribute).
From the start, Julien Ponge wanted Golo to be a playground for his students to teach them how to modify programming languages.The two simplest ways to do this, are the following:
The first, with Java
You just need to add a static method to the final class Predefined:
public static String panda() { return "🐼"; }
You can read the source code of the Predefined class here [10].
After the compilation of the Golo project, you’ll get a new keyword in Golo:
function main = |args| { println("this is a panda: " + panda()) }
And of course you can develop Golo in Golo.
The second, with Golo
Remember the example of the “functional map”:
augment java.util.Map { @option function getOptional = |this, key| -> this: get(key) }
If you add this augmentation to the ‘standard-augmentations.golo’ module of the Golo project, the getOptional method of java.util.Map will become a natural feature of the Golo language. You will find the source code of the ‘standard-augmentations.golo’ module here [11]. You can of course, write a lot of more complicated things and even play with the AST tree.
The possibilities of Golo are numerous, and it is not possible to list them all, but already, this article gives you a small glimpse of why gololang is my favourite language to write small applications.
In case this article made you curious, you can find some tutorials to start with Golo in the Github repository of the language [12].
{ References }
- https://twitter.com/jponge
- https://twitter.com/yannick_loiseau
- https://twitter.com/k33g_org
- https://github.com/eclipse/golo-lang
- https://www.eclipse.org/downloads/download.php?file=/golo/golo-3.3.0.zip
- https://gitpod.io/#https://gitlab.com/golo-lang/golo-in-action
- https://golo-lang.org/documentation/3.3.0/index.html#_running_em_hello_world_em
- https://golo-lang.org/documentation/3.3.0/golodoc/gololang/Errors.html
- https://gitlab.com/golo-lang/golo-in-action/-/tree/master/04-augment-java
- https://github.com/eclipse/golo-lang/blob/master/src/main/java/gololang/Predefined.java
- https://github.com/eclipse/golo-lang/blob/master/src/main/golo/standard-augmentations.golo
- https://github.com/eclipse/golo-lang/tree/master/tutorials