InvokeDynamic

Golo – die dynamische Programmiersprache für die Java-Plattform [Pirates of the JVM]

Redaktion JAXenter

Im Archipel der dynamischen JVM-Sprachen landen wir heute auf der Insel Golo. Entdeckt wurde Golo von der Universität Lyon, als man den seit Java 7 verfügbaren InvokeDynamic-Bytecode ausloten wollte. Welche Eigenschaften Golo zu einer interessanten Ergänzung zu Java machen, erklärt uns Golo-Projektleiter Yannick Loiseau

JAXenter: Welche Vorteile bietet Golo, die andere Programmiersprachen nicht haben?

Ein charakteristisches Merkmal von Golo ist seine dynamische Natur.

Yannick Loiseau: Einer der wichtigsten Gründe für die Entwicklung von Golo war, mit den neuen invokedynamic-Richtlinien für die JVM zu experimentieren. Das Ziel bestand darin, eine dynamische, zugleich aber auch effiziente und leichtgewichtige Sprache zu entwickeln. Angefangen hat das alles als Experiment des Dynamid-Forschungsteams, das einen Fokus auf Umgebungen mit hoch dynamischen Eigenschaften gelegt hat, wie man sie im IoT, bei eingebetteten Systemen und in der Cloud findet. Seit dem Release 3.1.0 im März 2016 ist Golo ein Eclipse Technology Project.

Ein charakteristisches Merkmal von Golo ist seine dynamische Natur. Alles wird in der Runtime aufgelöst. Ein Modul kann beispielsweise kompiliert werden, selbst wenn die Module, von denen es abhängig ist, nicht zur Compile-Zeit verfügbar sind. Das liegt daran, dass selbst die Funktionsauflösung (sowas wie Linking) dynamisch ist. Ein anderer Gesichtspunkt ist die enge Integration mit Java.

Klick auf die Karte für die komplette Infografik.

 

JAXenter: Was sind die Kernprinzipien der Sprache?

Yannick Loiseau: Golo ist eine sehr dynamische Sprache. Variablen sind nicht typenbasiert, Typen werden im Stil des Duck-Typings in der Runtime geprüft:

 var foo = 42
foo = "The answer is " + foo

Außerdem ist Golo eine funktionale Sprache. Demnach stellen Funktionen Werte erster Klasse dar, die als Funktionsparameter verwendet oder von anderen Funktionen zurückgegeben werden können:

 
function foo = |x| -> x + 1
# (...)
# ^foo is a reference to the foo function
println(list[1, 2, 3]: map(^foo))
# prints [2, 3, 4]

Closures werden unterstützt, genauso wie viele Funktionen höherer Ordnung, mit denen sie manipuliert werden können.

function adder = |a| -> |b| -> a + b
function multiply = |a| -> |b| -> a * b
#(...)
let transform = adder(3): andThen(multiply(2))
println(transform(18))
# prints 42

Wenn man unter die Haube schaut, sind Funktionen in Golo eigentlich ganz einfach statische Methoden in Java. Darum kann jede vorhandene Methode als Golo-Funktion genutzt werden:

import java.util.Objects
#(...)
println(list[1, null, 42, null]: filter(^nonNull))
# prints [1, 42]
println(list["a", "aa", "aaa"]: map(^String::length)
# prints [1, 2, 3]
let formater = ^String::format\1...: bindTo("the %s is %s"): asVarargsCollector()
println(formater("answer", 42))
# prints "the answer is 42"

Golo ist aber zugleich auch eine objektorientierte Sprache und setzt die Java Standard Library ein. So handelt es sich beispielsweise bei den list[…] im vorigen Code-Beispiel um Instanzen von java.util.LinkedList. So kann jedes Java-Objekt transparent eingesetzt werden. Das macht Golo zu einer tollen Kitt-Sprache, um etliche Java Libraries und Frameworks zu integrieren:

import javax.swing
# (...)
let button = JButton("Click me!")
button: addActionListener(|event| -> println("( )"))

Ein wichtiges Feature für die leichte Integration von Java sind Augmentations. Mit Augmentationen kann man neue Methoden dynamisch zu jeder Klasse hinzufügen.

Das hier ist beispielsweise absolut zulässiger Golo-Code:

5: times(|idx| {
 println("loop number %s": format(idx))
})

5 und loop number %s sind reguläre Instanzen von java.lang.Integer und java.lang.String. Augmentationen funktionieren mit Super-Klassen und sogar mit Interfaces:

augment java.lang.Number {
 function answer = |this| -> this * 2
}
#(...)
println(21: answer())
# prints 42

Golo erstellt keine Klassen, sondern algebraisches Datentypen (Tupel, Struct und Union), die bei Bedarf augmentiert werden können, um Polymorphismus zu ermöglichen.

Augmentationen können zudem Namen haben, demnach also eine Funktionsart ähnlich der von Traits und Aspects bereitstellen:

augmentation Fooable = {
 function foo = |this| -> "foo " + this
}
# elsewhere...
augment java.lang.String with Fooable
augment java.lang.Number with Fooable
#(...)
println("bar": foo())
println(42: foo())

Ein weiteres cooles Feature sind die Function Decorators, die Ähnlichkeit mit den Möglichkeiten in Python haben. Ein Decorator ist nur eine Funktion höherer Ordnung, die dazu verwendet werden kann, das Verhalten einer anderen Funktion zu beeinflussen. Das stellt einen weiteren „Aspekt-artigen“ Zugangsweg zur Verfügung:

function logger = |message| -> |fun| -> |args...| {
 println("# " + message)
 return fun: invoke(args)
}
@logger("foo")
function f1 = |a, b| {
 return a + b
}
@logger("bar")
function f2 = |a| -> "hello " + a
#(...)
let a = f1(18, 24)
# a = 42, prints "# foo"
let b = f2("world")
# b = "hello world", prints "# bar"

Golo bringt viele nützliche Decorators mit, beispielweise gololang.Errors::option. Damit wird eine Funktion, die Exceptions ausgeben kann, in eine Funktion umgewandelt, die ein `java.util.Optional` Objekt ausgibt.

JAXenter: Wie würde ein typisches Programm mit Golo aussehen?

Yannick Loiseau: Das klassische Hello World würde in Golo so aussehen:

module Hello
function main = |args| {
 println("Hello World")
}

Um noch ein paar andere Funktionen zu zeigen, kann man das nun ganz und gar sinnlos verkomplizieren:

module SaySomething
local function say = |prefix, suffix| -> |name| -> prefix + name + suffix
function main = |args| {
 let message = say("Hello ", "!")
 let names = match {
	when args: isEmpty() then list["World"]
	otherwise args: asList()
 }
 let messages = names: map(message)
 foreach m in messages {
	println(m)
 }
}

JAXenter: Für welche Art von Anwendungen bzw. Anwendungsfällen eignet sich Golo besonders gut? Für welche eher weniger?

Yannick Loiseau: Als einfache, dynamische Sprache überschneidet sich der Anwendungsbereich von Golo mit Sprachen wie Python oder Ruby, sowie Goovy aus der JVM-Welt.

Dank seiner guten Interaktion mit Java Libraries kann Golo als Kitt- und Script-Sprache verwendet werden. Da die Runtime sehr schlank ist (um die 800KB mit Abhängigkeiten, ohne die JRE einzurechnen), ist sie gut geeignet für kleine und eingebettete Systeme.

Auf der anderen Seite passieren die meisten Fehler allerdings erst in der Runtime, weil der Compiler aufgrund der dynamischen Natur der Sprache nur wenige Tests ausführt. Wer also Wert auf ein robustes System legt, sollte besser in eine gute Testabdeckung investieren oder eine statischere Sprache verwenden.

Eine weitere Konsequenz aus dem dynamischen Typensystem ist, dass auf dem Level der JVM jeder Wert ein Objekt ist. Das heißt, dass bei numerischen Berechnungen viel gepackt und entpackt wird – das wirkt sich natürlich massiv auf die Performance aus. Die Implementierung solcher rechenintensiven Aspekte in reinem Java in Kombination mit der Systemintegration in Golo ist natürlich auch hier eine Lösung für das Problem, wie so oft bei Sprachen dieser Art. Diese Integration ist zum Glück ziemlich leicht umzusetzen.

Die Pirates of the JVM

JAXenter: Wie ist der derzeitige Status Quo der Sprache?

Yannick Loiseau: Die Sprache an sich ist ziemlich stabil und hat zahlreiche Features. In den Standard Libraries fehlt allerdings noch das eine oder andere essentielle Modul. Es ist aber ja auch so, dass Golo sich aus dem reichen Ökosystem von Java bedienen kann, da die Sprache gut mit Java Libraries integrierbar ist.

JAXenter: Welche Pläne gibt es für die Zukunft von Golo?

Yannick Loiseau: Bezüglich des Funktionsumfangs haben wir einiges in Arbeit, wie die Optimierung der Endrekursion oder Macros, ein heiß ersehntes Feature. Auch mit der Java-Integration gibt es noch ein paar ungelöste Probleme, damit das so transparent wie möglich läuft.

Performance ist ebenfalls ein wichtiges Thema. Wir haben einige Experimente mit dem Truffle Graal Framework geplant.

Wie bereits gesagt, fehlen aber ja auch noch ein paar zentrale Funktionen in der Standard Library. Beiträge dazu sind uns sehr willkommen! Die größte Hürde stellt allerdings die Integration von Java 9 und Jigsaw dar, die einiges von dem kaputt macht, worauf Golo aufbaut. Das wird noch eine Menge Arbeit.

JAXenter: Wie steigt man am besten in die Arbeit mit Golo ein?

Yannick Loiseau: Golo installieren und anfangen zu coden!

Die Installation kann aus den Sources oder mit nativen Paketen auf Windows, Homebrew und RPM-basierte Distributionen vorgenommen werden. Golo ist außerdem via Bintray / jCenter verfügbar und kann zur Verwendung in Maven und Gradle auf Maven Central bezogen werden. Wer sein System sauber halten möchte, kann sogar auf Docker Images von Golo zurückgreifen. Die meisten Java-orientierten IDEs verfügen über einen (eingeschränkten) Golo-Support, der allerdings in manchen Fällen veraltet sein könnte.

Als nächstes sollte man die Dokumentation lesen; im Source Repository finden sich außerdem Code-Beispiele, die beim Einstieg helfen.

Mehr Informationen gibt es im Artikel Eclipse Golo – leichtgewichtig und dynamisch auf der JVM und im Golo Guide.

Yannick Loiseau ist Informatik-Professor an der Universität Clermont Auvergne, Frankreich, und Teil des LIMOS Forschungs-Labor. Loiseau arbeitet seit 2014 an der Golo-Programmiersprache und ist seit Oktober 2016 Co-Leiter des Projektes.

Verwandte Themen:

Geschrieben von
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.