2009 legte die von Google initiierte Programmiersprache Go einen wilden Start hin. Spätestens seit 2012, dem Erscheinungsjahr von Version 1.0, gilt Go als stabil. Sie bringt recht erbauliche Features mit, die Entwicklern lästige Routinearbeiten abnehmen. Ist Go inzwischen im Mainstream angekommen?
Mit Go kam 2009 eine auf den ersten Blick simple Sprache zur Welt [1], die wie ein modernes C wirkte. Neben ihrem Markenzeichen CSP (Communicating Sequential Processes, [2]) als Lösung für Nebenläufigkeit, schlugen auch Einflüsse von Limbo [3] und Inferno [4] durch. Die Entwicklung fing damals gerade erst an und Änderungen im Wochenrhythmus führten mitunter dazu, dass sich eben noch lauffähiger Code nicht mehr kompilieren ließ.
Erst 2012 wurde Go 1.0 mit dem Versprechen veröffentlicht, dass der geschriebene Code auch mit zukünftigen Versionen der 1.x-Reihe funktioniert. Seitdem liegt der Fokus der Go-Entwickler auf den Tools, Compilern und der Runtime.
Das Buildsystem
Im Jahr 2009 brachte Go noch kein eigenes Buildsystem mit. Vielmehr mussten Entwickler die Compiler und Linker manuell aufrufen oder für größere Projekte Makefiles schreiben, ähnlich wie in C. Da Go es jedoch erleichtern sollte, komplexe und verteilte Projekte zu entwickeln, musste eine bessere Lösung her. Das »go« -Tool lieferte ein einfach zu bedienendes Buildsystem ohne Konfiguration. Mit Hilfe seiner diversen Funktionen lassen sich Projekte bauen und mit diesen arbeiten. Die wichtigsten Funktionen zählt Tabelle 1 auf.
Tabelle 1
Wichtige Go-Tool-Funktionen
|
Funktion |
Effekt |
|---|---|
|
go build |
Damit kann ein Entwickler Pakete bauen, um zu schauen, ob der Compiler zufrieden ist (für die Bibliotheken), oder um schlicht ausführbare Dateien zu erzeugen. |
|
go install |
Verhält sich ähnlich wie »go build« , speichert aber das Ergebnis auch für Bibliotheken, damit spätere Kompiliervorgänge schneller ablaufen. |
|
go get |
Der Befehl lädt sowohl die Abhängigkeiten des aktuellen Projekts als auch allgemein Pakete aus dem Internet herunter. |
Wie erwähnt brauchen all diese Funktionen keine Konfiguration, ein Entwickler muss also weder Makefiles schreiben noch Abhängigkeiten explizit auflisten, noch auf den Quelltext verlinken. Das ermöglichen im Kern zwei simple Prinzipien: Die »GOPATH« -Umgebungsvariable (Abbildung 1), die sich ähnlich wie »PATH« verhält und einen oder mehrere Workspaces angibt sowie die Paketpfade. »GOPATH« ist ein wichtiges Konzept. Die Variable bestimmt, wo Go die Quellen eines Pakets findet und wo es Objektdateien speichert. Jeder Workspace besteht dabei aus drei Ordnern:
- »src/« speichert Quellcode
- »pkg/« speichert Objektdateien
- »bin/« speichert kompilierte, ausführbare Dateien
Führt der Entwickler also »go build« aus, sucht das Buildskript die Quelltexte in »$GOPATH/src/« , und »go install« schreibt Dateien nach »$GOPATH/pkg/« sowie »GOPATH/bin/« .
Alle Quelltexte sind in Pakete unterteilt. Jeder Ordner und Unterordner in »$GOPATH/src/« stellt ein einzelnes Paket dar, jeder Quelltext muss sich in einem solchen Paket befinden, damit Go ihn findet. Aber anders als zum Beispiel Workspaces in Java bezieht sich ein Workspace in Go nicht ausschließlich auf ein einzelnes Projekt. Vielmehr referenziert es eine Sammlung von Go-Paketen und -Projekten, sozusagen die eigene Kopie eines Teils des Go-Universums. Daher finden sich meist verschiedene Projekte in einem einzelnen Workspace.
Jedes Paket lässt sich über einen individuellen Pfad erreichen. Während Pakete in der Standardbibliothek nur sehr kurze Pfadnamen tragen, etwa »fmt« oder »net/http« , sollten eigene Pakete eindeutige Namen besitzen, die nicht mit den Namen der Pakete anderer Entwickler kollidieren. Daher verwenden Programmierer häufig den Pfad, über den sie das Paket im Internet erreichen, etwa auf Github [5]. Ein Pfad heißt dann zum Beispiel »github.com/Benutzer/example« . Dieser Pfad ist eindeutig, da niemand anderes den Benutzernamen verwenden kann. Zugleich erlaubt es diese Art der Benennung später, die Pakete automatisch aus dem Internet zu beziehen.
In Aktion
Die folgenden Listings verdeutlichen die Dateistruktur und zeigen die Tools in Aktion. Nach Vorbild von Listing 1 erstellt ein Entwickler die Grundstruktur für ein Paket. Der Pfad lautet »github.com/Benutzer/hallo« , in der Annahme, dass das Paket dereinst auf Github gehostet werden wird. Listing 2 zeigt den Quelltext des eigentlichen Programms, das, weil es in der Datei »$GOPATH/src/github.com/Benutzer/hallo« steckt, den Namen »hallo.go« trägt. Es könnte aber auch anders heißen, etwa »main.go« .
Listing 2
hallo.go
01 package main
02
03 import "fmt"
04
05 func main() {
06 fmt.Println("Hallo, Benutzer.")
07 }
Listing 1
Pfad anlegen
01 $ export GOPATH=~/go/ 02 $ mkdir -p $GOPATH/src/github.com/Benutzer/hallo
Listing 3 kompiliert das Projekt. Hierzu übergibt der Entwickler den Pfad zum Paket und erhält als Dank eine ausführbare Datei im aktuellen Verzeichnis. Die heißt nach dem Ordner mit dem Quelltext, in diesem Fall also »hallo« . Wer das Programm ausführt, den begrüßt es auf etwas steife Art und Weise.
Listing 3
go build
01 $ go build github.com/Benutzer/hallo 02 $ ./hallo 03 Hallo, Benutzer.
Auch Listing 4 kompiliert das Projekt, allerdings über »go install« und nicht »go build« . Das Endresultat landet im selbst festgelegten Ordner »$GOPATH/bin/« . Davon abgesehen verhält sich das Programm identisch.
Listing 4
go install
01 $ go install github.com/Benutzer/hallo 02 $ $GOPATH/bin/hallo 03 Hallo, Benutzer.
Listing 5 zeigt eine etwas veränderte Version des Programms, die eine externe Bibliothek verwendet, um Parameter auf der Kommandozeile zu übergeben. Go bringt hierzu zwar auch das Paket »flag« in der Standardbibliothek mit, dieses implementiert aber die Flag-Regeln von Plan 9, nicht die von Gnu. Der Versuch, es zu kompilieren, schlägt fehl, wie Listing 6 veranschaulicht.
Listing 6
Vergessene Abhängigkeiten
01 $ go build github.com/user/hallo 02 github.com/user/hallo/main.go:5:3: cannot find package "github.com/ogier/pflag" in any of: 03 /usr/lib/go/src/github.com/ogier/pflag (from $GOROOT) 04 /home/dominikh/go/src/github.com/ogier/pflag (from $GOPATH)
Listing 5
hallo.go (Version 2)
01 package main
02
03 import (
04 "fmt"
05 "github.com/ogier/pflag"
06 )
07
08 func main() {
09 name := pflag.String("name", "Unbekannt", "Dein Name")
10 pflag.Parse()
11 fmt.Println("Hallo,", *name)
12 }
Das liegt daran, dass der Nutzer das Paket »pflag« einfach noch nicht heruntergeladen hat. Bei dieser Aufgabe hilft »go get« . Es holt Pakete aus dem Netz und installiert sie, was rekursiv geschieht, wodurch es auch alle Abhängigkeiten einspielt. Ruft der Entwickler, wie es Listing 7 zeigt, zuerst »go get« für das eigene Paket auf, beschafft er auch alle Abhängigkeiten, im konkreten Fall »pflag« .
Listing 7
go get
01 $ go get github.com/Benutzer/hallo 02 $ go build github.com/Benutzer/hallo 03 dominikh-pc ./junk/src $ ./hallo 04 Hallo, Unbekannt 05 dominikh-pc ./junk/src $ ./hallo --name="lieber Leser" 06 Hallo, lieber Leser 07
Dieser Ablauf skaliert problemlos für Hunderte von Paketen und Abhängigkeiten. Der Entwickler muss nie sagen, wie genau der Buildprozess ablaufen soll, welches Versionskontrollsystem er verwendet oder wie er Abhängigkeiten herunterladen möchte.
Nie mehr Streitereien
So banal es auch klingt: Im Arbeitsalltag vergeuden Programmierer unvorstellbar viel Zeit damit, sich über richtige Codeformatierungen zu streiten. Leerzeichen oder Tabs? Wie viele Leerzeichen? Wohin kommen die geschweiften Klammern? Jeder Entwickler hat hier eigene Vorlieben, in Teams mit mehreren Programmierern kommt es darüber nicht selten zu Streit. Weil das so ist, gilt es, für jedes neue Projekt oder jeden neuen Job neue Richtlinien auswendig zu lernen – auch das braucht Zeit.
Go kennt dieses Problem nicht. Es bringt ein Tool namens »gofmt« mit, das den Code für Entwickler formatiert, und zwar auf die einzig richtige Art und Weise (Abbildung 2). Es bietet keine Optionen an, die das Ergebnis beeinflussen, und jeder neue Go-Entwickler wird aufgefordert »gofmt« zu benutzen. Somit fallen die Diskussionen zu Codeformatierungen weg, wird der Code weltweit einheitlich gestaltet. Das spart zugleich Zeit beim Lesen von Code, da zumindest das Aussehen bekannt ist.
Tools, Tools und nochmals Tools
Inspiriert durch die offiziellen Tools, zum Beispiel das Buildsystem und »gofmt« , programmieren viele Entwickler ihre eigenen Werkzeuge. Prominent sind Tools wie Autocompletion, Refactoring und Linter, doch es gibt auch viele andere Helfer, die kleine, wiederkehrende Aufgaben erledigen.
Das klappt vor allem, weil die Standardbibliotheken von Go einen Parser für die Sprache selbst an Bord haben. Außerdem folgt Go dem Unix-Prinzip “Write programs that do one thing and do it well”, weswegen solche Tools nicht Teil einer riesigen IDE sind. Stattdessen arbeiten sie meist auf der Kommandozeile und lassen sich mit jedem beliebigen Editor kombinieren.
Eine Community auf Go
Eine Programmiersprache kann so gut sein, wie sie will, ohne Community hebt sie nicht ab. Go hat so eine Community. Neben Google gehört eine lange Liste bekannter Firmen [6] zu den Go-Nutzern, darunter etwa die BBC, Canonical und Dropbox.
Das Ziel von Go bestand von Anfang an darin, das Entwickeln von Serveranwendungen zu vereinfachen, genau das tut es heute. Etwa im Fall von Docker [7], die Containervirtualisierung ist komplett in Go geschrieben. Auch Ubuntu-Hersteller Canonical setzt auf Go, sowohl für Juju [8] als auch für LXD [9]. Disqus nutzt es für sein Kommentarsystem – die Aufzählung ließe sich fortsetzen.
Eine Fülle von Usergroups, Meet-ups und Konferenzen stärkt die Community zusätzlich. Allein im vorigen Jahr gab es drei gut besuchte Veranstaltungen nur für Go: die Gophercon [10], Dot Go [11] und die Gocon Tokyo. Nicht zuletzt finden auf der Fosdem regelmäßig ausgebuchte Veranstaltungen zu Go statt. Es scheint also tatsächlich, als sei die Sprache im Mainstream angekommen und werde vorerst auch dort bleiben.
Infos
- Marcus Nutzinger, Rainer Poisel, “Ready, Set, Go!”: Linux-Magazin 06/10, S. 118, https://www.linux-magazin.de/Ausgaben/2010/06/Ready-Set-Go/
- CSP: http://de.wikipedia.org/wiki/Communicating_Sequential_Processes
- Limbo: http://de.wikipedia.org/wiki/Limbo_%28Programmiersprache%29
- Inferno: http://www.vitanuova.com/inferno/index.html
- Github: https://github.com
- Bekannte Go-Nutzer: https://github.com/golang/go/wiki/GoUsers
- Docker: https://www.docker.com
- Juju: https://juju.ubuntu.com
- Canonicals LXD: http://www.ubuntu.com/cloud/tools/lxd
- Gophercon: http://www.gophercon.com
- Dot Go: http://www.dotgo.eu







