Ein Einstieg in die Programmiersprache Go, Teil 2

Seite 3: Die unschönen Seiten von Go

Inhaltsverzeichnis

Neben den vielen Vorteilen von Go gibt es einige Dinge, die nicht gut gelöst sind. Herauszustellen sind vor allem: fehlende generische Datentypen und das Dependency-Management.

Generische Datentypen erlauben parametrische Polymorphie. Das ist gerade bei statisch typisierten Programmiersprachen wünschenswert. Viele Algorithmen und Container-Datentypen müssen Entwickler nur einmal programmieren und dann über den Typ parametrisieren. Ansonsten müsste man sie für jeden Typ ausprogrammieren.

Alle ernstzunehmenden statisch typisierten Programmiersprachen der letzten Jahre unterstützen generische Datentypen – außer Go. Zwar sind die eingebauten Container-Datentypen generisch und über das Verwenden von Interface-Typen lässt sich Codeduplizierung vermeiden, trotzdem fühlt man sich oft in die Zeiten von Java vor Version 1.5 versetzt. Mit dem Unterschied, dass Go das leere Interface statt wie in Java den Typ Object nutzt. In den konkreten Typ wird dann per Cast manuell umgewandelt: ein aufwendiges und fehleranfälliges Verfahren.

Den Kritikpunkt, den viele oft und regelmäßig noch vor Version 1.0 artikulierten, soll Go 2 beheben. Ein Proposal für die Einführung generischer Datentypen liegt vor. Wann Go 2, das nicht mehr mit Go 1.x kompatibel sein wird, erscheint und wie die generischen Datentypen letztlich umgesetzt werden, steht indes noch nicht fest.

Der zweite große Kritikpunkt, der ebenfalls von Anfang an besteht, ist das fehlende Dependency-Management. Es ist zwar seit jeher möglich gewesen, Dependencies ganz einfach herunterzuladen, auch werden dabei Git und GitHub sowie andere Versionskontrollsysteme und Repository-Hoster unterstützt. Ein Beispiel:

$ go get github.com/golang/example/hello

Das Problem ist allerdings, dass es keine Möglichkeit gibt, eine bestimmte Version einer Abhängigkeit anzugeben. Go lädt immer die aktuelle Version. Inkompatibilitäten sind programmiert und reproduzierbare Builds unmöglich. Das Ergebnis ist abhängig davon, wann man die Dependency geladen hat. Bei einem großen Monorepo, das entsprechend gepflegt ist (wie bei Google), ist das selten ein Problem. Will man jedoch Bibliotheken von Drittanbietern nutzen, kann jede Änderung dort den eigenen Build kaputt machen.

Eine äußerst unzufrieden stellende Situation, die dazu geführt hat, dass eine Reihe von Dependency- beziehungweise Paketmanagern in der Go-Community entstanden sind – godep, glide, goom, goop, um nur einige zu nennen. Sie sind (meistens) inkompatibel zueinander, sodass sie nur weiterhelfen, wenn der gleiche Paketmanager alle Dependencies eines Projekts verwaltet.

Go ist seit Version 1.5 in der Lage, Dependencies aus einem speziellen vendor-Verzeichnis zu laden, das mit im Quelltextbaum liegt. Die dorthin kopierten Dependencies können Entwickler zusammen mit dem Projekt versionieren. Das löst zwar das Versionsproblem, ist aber wenig komfortabel und entspricht nicht einem zeitgemäßen Dependency-Management.

In der im letzten Jahr veröffentlichten Version 1.11 hat ein Modulsystem in Go Einzug gehalten. Module sind ein oder mehrere Packages, die ein gemeinsames Import-Präfix haben und nach Semantic Versioning versioniert werden. Go hat somit endlich ein modernes Dependency-Management. Bedauerlicherweise wurde es entwickelt, ohne die Community einzubeziehen, was dort für großen Unmut gesorgt hat, denn sie hatte viel Zeit und Energie in eine Lösung investiert. Es bleibt zu hoffen, dass das Modulsystem trotzdem angenommen wird und sich als Standard durchsetzt.