zurück zum Artikel

Einstieg in Crystal: Kompilierte Sprache mit modernen Konzepten

Tam Hanna

(Bild: Shutterstock.com)

Die von Ruby inspirierte Sprache Crystal empfiehlt sich dank leichtem Einstieg und eigenem Compiler als performante Alternative für General-Purpose-Projekte.

Die Programmiersprache Crystal besitzt eine schnell zu erlernende, sehr stark von Ruby inspirierte Syntax und dazu noch die Möglichkeit, Applikationen direkt in Maschinencode zu kompilieren. Damit soll sie als General-Purpose-Sprache leistungsfähig genug für verschiedenste Aufgaben sein. Der eigene Compiler erspart Optimierungsmaßnahmen, wie sie beispielsweise mit JIT und Co. nötig sind – auch dann, wenn ein Crystal-Programm sehr hohe Performance erreichen soll. Entwicklerinnen und Entwickler, die gerne regelmäßig neue Programmiersprachen erlernen, finden in Crystal also eine interessante Option, die sich ausreichend von etablierten Sprachen absetzt.

Von JavaScript verbrannten Entwicklern dürfte außerdem positiv auffallen, dass Crystal am Ende doch streng typisiert ist – so kann der Compiler beispielsweise Nullchecks durchführen und so viele häufige Programmfehler schon während der Kompilation erkennen.

Für Crystal sprechen auch die umfangreichen Möglichkeiten der Metaprogrammierung. Darunter versteht man in der Welt von Crystal, dass sich Code selbst modifizieren beziehungsweise selbst generieren kann. Zwar ist die Flexibilität beim Kompilieren in nativen Code nicht so hoch wie bei zur Laufzeit interpretierten Sprachen, trotzdem gilt, dass Crystal Präprozessor-Lösungen, wie sie bei den mit C verwandten Sprachen weit verbreitet sind, haushoch überlegen ist.

Abkündigungen (Deprecations) von Komponenten in einem Ökosystem können für Entwicklerinnen und Entwickler dramatische Folgen nach sich ziehen. Während sich der Wegfall einer Bibliothek meist gut verkraften lässt, kommt die Abkündigung einer Programmiersprache in einem laufenden Softwareprojekt einem wirtschaftlichen Totalschaden gleich – die komplette Codebasis wird unbrauchbar, wie das "Abenteuer" um Visual Basic 6 [1] anschaulich gezeigt hat. Aus diesem Grund sind Entwickler und Entwicklerinnen gut beraten, sich nicht auf zu exotische Programmiersprachenprojekte einzulassen.

Die Programmiersprache Crystal wurde im Jahre 2011 von dem argentinischen Beratungsunternehmen Manas Technology Solutions gestartet. Die Entwickler Ary Borenszweig, Juan Wajnerman und Brian Cardiff ließen sich zunächst noch stark von Ruby inspirieren, suchten dann aber schnell nach Wegen, eine höhere Performance der resultierenden Systeme zu erzielen.

Vor knapp zwei Jahren hat Crystal die Reife eines Major Release 1.0 erreicht [2] und erfreut sich wachsender Beliebtheit. Rund um das GitHub-Repository [3] der unter Apache-2.0-Lizenz als Open Source verfügbaren Programmiersprache ist ein aktives Ökosystem mit mehr als 500 Beitragenden entstanden – und Crystal verzeichnet aktuell in Version 1.6.2 schon mehr als 18.000 GitHub-Sterne (s. Abb. 1).

Über 18.000 Sterne im GitHub-Repository von Crystal zeugen von der wachsenden Beliebtheit des Programmiersystems (Abb. 1).

(Bild: GitHub)

Mit Crystal erstellte Programme funktionieren auf den Prozessorarchitekturen x86, x64 sowie den beiden ARM-Varianten (32 und 64 Bit) unter Linux, macOS und FreeBSD. Der Support für Windows ist zum Zeitpunkt der Fertigstellung dieses Artikels hingegen noch experimentell.

Crystal ist unter Windows noch nicht final (Abb. 2).

Für die ersten einführenden Schritte in die Arbeit mit Crystal kommt im Folgenden Ubuntu 22.04 LTS zum Einsatz. Wer die Beispiele mit einem anderen Betriebssystem nachvollziehen möchte, findet auf der Crystal-Website [4] Installationsanweisungen für diverse Plattformen.

Für Ubuntu steht ein per cURL ausführbares Installationsskript parat, das sich folgendermaßen aktivieren lässt:

curl -fsSL https://crystal-lang.org/install.sh | sudo bash

Wie in der folgenden Ausgabe zu erkennen, lädt das Werkzeug eine aktuelle Nightly-Version von Crystal herunter:

crystal -v
Crystal 1.6.2 [879691b2e] (2022-11-03)

LLVM: 13.0.1
Default target: x86_64-unknown-linux-gnu

Über spezifische Parameter lassen sich alternativ aber auch stabile oder gezielt ausgewählte Crystal-Versionen über das Skript installieren. Weitere Informationen hierzu finden sich in der Installationsdokumentation [5].

Entwicklerinnen und Entwickler, die ihre Software lieber via snap beziehen, können auch auf diese Paketierungsvariante zurückgreifen. In den folgenden Beispielen wird darauf verzichtet, um Komplikationen mit AppArmor und Co. zu vermeiden.

Bei der Arbeit mit Crystal ist außerdem eine vollwertige Entwicklungsumgebung (IDE) empfehlenswert. In der Welt von Crystal hat sich Microsofts quelloffenes Visual Studio Code als Quasistandard etabliert. Es soll auch in den folgenden Schritten zum Einsatz kommen.

Nach dem Start von Visual Studio Code erscheint das Plug-in Crystal Language als rotierendes Icon. Ein Klick auf Install startet den bei Visual-Studio-Code-Erweiterungen typischen Deployment-Prozess.

Wer darüber hinaus IntelliSense nutzen möchte, muss zusätzlich das Language-Server-Modul installieren, das der IDE die Parsing-Informationen zur Verfügung stellt. Es findet sich samt Installationsanweisung derzeit im GitHub-Repo von Julien Elbaz (elbywan) [6].

Nach erfolgreichem Einrichten der Crystal-Arbeitsumgebung kann ein erstes "Hello World"-Programm starten. Wie für Crystal-Code üblich, ist dazu eine Datei mit der Endung .cr anzulegen und diese in einer schon laufenden Instanz von Visual Studio Code zu öffnen:

touch helloworld.cr
code helloworld.cr

Das Beispiel zeigt bereits einige Besonderheiten des Typsystems:

zahl1 = 20
zahl2 = 30
p "Hallo Welt", zahl1, zahl2
KONSTANTE = 24
zahl1, zahl2 = zahl2, zahl1
p "Hallo Welt", zahl1, zahl2

Wichtig beim Einsatz von Variablen in Crystal ist vor allem, dass Variablennamen immer mit einem Kleinbuchstaben anfangen müssen. Ist der erste Buchstabe der Variable ein Großbuchstabe, so stuft die Runtime sie als Konstante ein. Versuche, den Wert zu ändern, führen dann zu Kompilierungsfehlern.

Im obigen Listing fallen zwei Besonderheiten auf: Zum einen erfolgt die Ausgabe von Informationen in der Kommandozeile einfach durch Voranstellen von p und einer Parameterliste. Zweitens veranlasst die Zeile zahl1, zahl2 = zahl2, zahl1 einen Tausch der in den Variablen gespeicherten Werte.

Zuweisungsoperationen mit mehreren Werten sind im Programmieralltag geläufig, weshalb Crystal hierfür eine syntaktische Besonderheit zur Verfügung stellt. Durch Ausführen des obigen Statements lässt sich das Austauschen in den gespeicherten Werten erreichen – wie sich durch Kompilieren respektive Ausführen des Programms überprüfen lässt:

crystal helloworld.cr

Wer dem Crystal-Compiler nur einen Dateinamen einer .cr-Datei als Parameter zur Ausführung übergibt, weist das Programm dazu an, eine Code-Datei direkt auszuführen. Das führt zum in Abbildung 3 gezeigten Ergebnis.

Der Variablenaustausch unter Einsatz der syntaktischen Besonderheiten von Crystal funktioniert reibungslos (Abb. 3).

Wer stattdessen beim Kompilieren eine ausführbare a.out-Datei erzeugen möchte, muss dazu den zusätzlichen Parameter build übergeben:

crystal build helloworld.cr
./helloworld

Die erzeugte Executable-Datei lässt sich auch unabhängig vom Crystal-Kompilierungssystem nutzen.

Eine vollumfängliche Besprechung der Crystal-Syntax würde den Umfang dieses Artikels sprengen, daher sollen im Folgenden nur die wichtigsten und interessantesten Aspekte in den Fokus rücken. Dazu zählen unter anderem Ranges, wie sie auch in Python vorkommen. Eine Range ist ein Variableninhalt, den der Compiler anhand der Benutzereingaben automatisch generiert und der das Hantieren mit for-Schleifen oder großen Feldern von Konstanten überflüssig macht.

Crystal erlaubt Entwicklern das Erzeugen mehr oder weniger beliebiger Ranges. Zur Anschauung sollen eine Zahlen- und eine Buchstaben-Range genügen:

range1 = 1..10
range2 = 'a'..'z'
p range1, range2

Der Crystal-Compiler liefert das in Abbildung 4 gezeigte Ergebnis:

Crystal betrachtet Ranges als "abstrakte Bereichsbeschreibung" (Abb. 4).

Eine Range zerlegt Crystal normalerweise nicht in ihre einzelnen Mitglieder, sondern sie gilt im Sinne der Mengenlehre als theoretische Beschreibung ihres Inhalts. Das lässt sich beispielsweise durch Ausführen einiger Methoden der Range bestätigen:

range1 = 1..10
range2 = 'a'..'z'
p range1.sum
range1.each do |laeufer|
    p laeufer
end

Abbildung 5 zeigt das Ergebnis des Kompilierens: Die sum-Operation addiert die Elemente, während each do eine Art Iterator realisiert. Die sum-Operation funktioniert nicht mit allen Range-Typen gleichermaßen. Wer die Zeichen-Range anwendet, erhält vom Compiler die Fehlermeldung "Error: undefined method 'zero' for Char.class".

Mengenoperationen auf Range-Basis funktionieren problemlos (Abb. 5).

An dieser Stelle kann man kurz überprüfen, ob Whitespaces für das Ausführen von Crystal-Programmen relevant sind. Hierzu genügt es, die Indentierung vor dem Statement zu entfernen:

range1.each do |laeufer|
p laeufer
end

Auch dieser Code lässt sich problemlos ausführen – Whitespaces spielen demnach keine Rolle. Crystal verhält sich bei der Indentierung offensichtlich genauso wie Visual Basic und vergleichbare Sprachen.

Das Vorhandensein von Ranges ist ein Indiz dafür, dass die Programmiersprache auch mit Selektionen und Iterationen umgehen können sollte.

Der folgende Code dient als Probelauf für Selektionen:

if range1.includes? 12
    p "Inkludiert"
else
    p "Inkludiert nicht"
end
unless range1.includes? 12
    p "Unless matcht!"
end

Neben der an C erinnernden If-Else-Selektion findet sich hier auch ein unless, ein "umgekehrtes If", das seine Payload immer dann ausführt, wenn eine Bedingung entweder false oder nil zurückliefert.

Da 12 offenkundig nicht Teil des Wertebereichs von 1 bis 10 ist, gibt das Programm nach dem Ausführen die Werte "Inkludiert nicht" und "Unless matcht!" zurück.

Die in den Selektionen zu verwendenden Auswahlkriterien sind bei Crystal flexibel. Für einen Vergleich auf Gleichheit sieht der Code folgendermaßen aus:

if 2 == 2
    p "Inkludiert"
end

Selektionen lassen sich in Crystal auch zum Ermitteln von Ergebnissen nutzen, die zur weiteren Verwendung in eine Variable einfließen:

value = if 2 == 2
    3
else 
    4
end
p value

Die allein in separaten Zeilen stehenden Zahlen 3 und 4 könnten auch als Strings oder andere Werte im Code Verwendung finden. Generell gilt jedoch, dass der durch p value ausgegebene Wert der Variable value den von der Selektion zurückgegebenen Wert enthält:

crystal helloworld.cr
3

Vertiefende Einführung in die Crystal-Syntax

(Bild: crystal-lang.org [7])

Wer sich im Detail mit den verschiedenen Syntax-Elementen von Crystal beschäftigen möchte, findet weitere Informationen in der auf der Projektwebseite bereitstehenden Sprachbeschreibung [8].

Das aus C und vergleichbaren Sprachen bekannte – und für viele Fehler verantwortliche – Case-Statement findet sich auch in Crystal. Die bereits erwähnte Möglichkeit, mehrere Werte durch Kommata zusammenzufassen, lässt sich auch hier zur Kombination von Bedingungen heranziehen. Fallthrough hingegen unterstützt Crystal nicht:

case exp
when value1, value2
  do_something
when value3
  do_something_else
else
  do_another_thing
end

Interessant ist in diesem Zusammenhang jedoch, dass Crystal auch Ranges in Bedingungen einbeziehen kann.

Der Einsatz von Schleifen in Crystal ist weitgehend mit der von C bekannten Vorgehensweise vergleichbar. Die while-Schleife ist wie folgt deklariert:

while some_condition
  do_this
end

Vergleichbar zur beschriebenen Vorgehensweise bei der if-Selektion lässt sich auch while zum Zurückgeben von Werten nutzen. Dazu dient das break-Statement, das sich konditional aufrufen lässt und den an den Aufrufer zu retournierenden Wert festgelegt:

a = 0
x = while a < 5
  a += 1
  break "three" if a == 3
end
x # => "three"

Wichtig ist, dass eine durch false-werden ihrer Bedingung endende while-Schleife den Wert nil zurückliefert:

x = while 1 > 2
  break 3
end
x # => nil

Analog zu unless gibt es mit until auch eine invertierte Version der while-Schleife, die so lange arbeitet, bis die übergebene Bedingung den Wert true annimmt. Dabei ist until explizit keine fußgesteuerte Schleife – eine solche ist in Crystal derzeit nicht implementiert:

until some_condition
  do_this
end

Auch Zählschleifen mit for sind im Sprachstandard von Crystal nicht vorgesehen.

Crystal bringt ebenfalls eine Paket- und Projektverwaltung mit, die sich analog zu pip und Co. verhält. Eine kleine Webapplikation auf Basis des Frameworks Athena soll die Besonderheiten der Paketverwaltung von Crystal näher beleuchten.

Der im Crystal-Compiler enthaltene Projektverwalter erzeugt anhand der nachfolgenden Kommandos eine neue Projektstruktur:

tamhan@tamhan-VirtualBox:~$ mkdir crystalspace
tamhan@tamhan-VirtualBox:~$ cd crystalspace/
tamhan@tamhan-VirtualBox:~/crystalspace$ crystal init app tamswebapp
    . . .
    create  /home/tamhan/crystalspace/tamswebapp/shard.yml
    . . .
tamhan@tamhan-VirtualBox:~/crystalspace$ cd tamswebapp/
tamhan@tamhan-VirtualBox:~/crystalspace/tamswebapp$ 

Der Aufruf crystal init app generiert ein neues Unterverzeichnis im aktuellen Arbeitsverzeichnis der Shell, in dem alle zum Projekt gehörenden Dateien unterkommen. Neben einer Readme- und verschiedenen Steuerungsdateien für das Git-Versionskontrollsystem ist vor allem die Datei /home/tamhan/crystalspace/tamswebapp/shard.yml relevant. Sie legt fest, welche der als Shard (englisch für (Ton-)Scherbe) bezeichneten Bibliotheken das vorliegende Projekt zum erfolgreichen Kompilieren benötigt.

Die Weiterentwicklung von Crystal-Modulen erfolgt im Allgemeinen auf GitHub. Das Athena-Modul und dessen jeweils aktuelle Version finden sich allerdings auf einer eigenen Website [9].

Sobald die Datei shard.yml zur Bearbeitung freigegeben ist, lässt sich die Athena-Bibliothek hinzufügen. Nach dem Öffnen der Konfigurationsdatei präsentiert sie unter anderem den targets-Block. Er legt fest, welches Deliverable aus dem vorliegenden Crystal-Projekt zu generieren ist. Das eigentliche Einbinden des Athena-Frameworks erfolgt dann durch das Einfügen eines dependencies-Blocks:

targets:
  tamswebapp:
    main: src/tamswebapp.cr

dependencies:
  athena:  
    github: athena-framework/framework
    version: ~> 0.17.0
    
crystal: 1.6.2

Während der version: ~> 0.17.0-Operator die zu verwendende Version festlegt, ist die in Crystal enthaltene Paketverwaltung in der Lage, speziell formatierte GitHub-Repositories zu durchforsten und die in ihnen enthaltenen Inhalte direkt in den Kompilierungsprozess einzubinden.

Anders als Gradle lädt Crystal in der Regel nicht bei jedem Build-Prozess alle Bibliotheksressourcen herunter und vermeidet so unnötige Latenzen. Kehrseite dieser Optimierung ist jedoch, dass Entwicklerinnen und Entwickler das Herunterladen und Aktualisieren der diversen Dependencies gegebenenfalls manuell anstoßen müssen:

tamhan@tamhan-VirtualBox:~/crystalspace/tamswebapp$ shards install
Resolving dependencies
. . .

Das Kommandozeilenwerkzeug shards ist in der Lage, die in den verschiedenen Bibliotheken enthaltenen Unterabhängigkeiten zu berücksichtigen (s. Abb. 6). Das Tool ist jedoch nicht Teil der Hauptdistribution von Crystal.

Shards komplettiert die Arbeitsumgebung (Abb. 6).

Die Umsetzung der Webapplikation erfolgt bei Nutzung des Athena-Frameworks im MVC-Pattern (Model, View, Controller). Crystal bringt darüber hinaus eine durchaus vollständige Implementierung der diversen objektorientierten Designparadigmata mit. Damit sind auch komplexe Web-Anwendungen umsetzbar. Wichtig für den Anfang ist, dass der Einsprungspunkt in die Webapplikation immer in Form einer Athena:: Framework:: Controller-Klasse vorliegen muss. Die Applikationslogik muss von dieser "erben" und der neu erzeugten Klasse – wie etwa aus JavaScript bekannt – die verschiedenen, für die Verarbeitung der eingehenden URLs notwendigen Event Handler einschreiben.

Zum Ableiten muss das Programm allerdings einen Einsprungspunkt erhalten. Eine Analyse der Build-Steuerungsdatei informiert darüber, dass der Hauptteil des Codes in der folgenden Datei zu finden ist:

tamhan@tamhan-VirtualBox:~/crystalspace/tamswebapp/src$ code tamswebapp.cr

In Visual Studio Code präsentiert sich nach dem Öffnen der folgende Korpus:

# TODO: Write documentation for `Tamswebapp`
module Tamswebapp
  VERSION = "0.1.0"

  # TODO: Put your code here
end

Module übernehmen in Crystal dabei die Rolle der unter anderem aus C# bekannten Namespaces. Um den in anderen Programmiersprachen erforderlichen Einsprungspunkt (Methode Main) zu simulieren, lässt sich mit dem folgenden Code im Modul-Korpus ein aktiver Aufruf unterbringen:

require "athena"
# TODO: Write documentation for `Tamswebapp`
module Tamswebapp
  VERSION = "0.1.0"
  # TODO: Put your code here
  p "Hallo Welt"
end

Neben einer Begrüßungsmeldung taucht in der Ausgabe auch erstmals das Statement Require auf. Es legt fest, dass das vorliegende Produkt von der Athena-Bibliothek abhängig ist.

Im nächsten Schritt lässt sich der Code mit Crystal Run kompilieren:

tamhan@tamhan-VirtualBox:~/crystalspace/tamswebapp$ crystal run src/tamswebapp.cr 
"Hallo Welt"

Ungewöhnlich ist am vorliegenden Code lediglich, dass der Aufruf nicht unter Verwendung der Spezifikationsdatei, sondern der Codedatei erfolgt. Die Ausgabe des Begrüßungsstrings informiert darüber, dass das Ausführen des Einsprungspunkts erfolgreich verlief.

Wie bei JavaScript gilt auch im Fall von Crystal, dass auf modulglobaler Ebene angelegter Code im Rahmen der Initialisierung beziehungsweise der Bereitstellung des Moduls zur Ausführung gelangt.

Das Erzeugen der Zugriffslogik beginnt mit:

module Tamswebapp
  VERSION = "0.1.0"
  class ExampleController < ATH::Controller
    @[ARTA::Get("/")]
    def index : String
      "Hello World"
    end
  end

Das Symbol < informiert Crystal über die anzulegende Vererbungsbeziehung.

Für das Festlegen der Route kommt eine Action zum Einsatz, die wie im vorangehenden Listing gezeigt angelegt wird. Der mit def beginnende Teil deklariert eine gewöhnliche Methode, die den Namen index aufweist, keine Parameter entgegennimmt und den Aufrufer mit einem String ausstattet. Interessant ist hier die Art des Zurückgebens des Strings: Crystal nutzt kein Statement wie return, sondern es reicht aus, den Rückgabewert allein in einer Zeile stehen zu lassen.

Anstelle des vorher als Instrumentierung dienenden Print-Aufrufs ist nun ATH.run aufzurufen. Die Methode durchsucht die gesamte Solution nach als Route markierten Methoden und bringt diese danach selbsttätig zur Ausführung:

  ATH.run
end

Nach dem Speichern der .cr-Datei lässt sich die Kompilierung anstoßen:

tamhan@tamhan-VirtualBox:~/crystalspace/tamswebapp$ crystal run src/tamswebapp.cr 
/usr/bin/ld: cannot find -lpcre2-8 (this usually means you need to install the development package for libpcre2-8): No such file or directory

crystal run lässt sich an dieser Stelle etwas Zeit, um anschließend die gezeigte Fehlermeldung zu retournieren.

Ursache dafür ist, dass Frameworks in der Welt von Crystal eigene Dependencies zu weiteren Binärdateien deklarieren dürfen. Leider löst shard diese im Rahmen der Bereitstellung des Moduls nicht selbsttätig auf. Ubuntu informiert an dieser Stelle allerdings durch Ausgabe einer Fehlermeldung darüber, dass zum Ausführen des Programms die PCRE-Bibliothek erforderlich ist.

Dieses Problem lässt sich durch Einsatz von apt-get beheben:

tamhan@tamhan-VirtualBox:~/crystalspace/tamswebapp$ sudo apt-get install libpcre2-dev

Nach dem erfolgreichen Herunterladen der Bibliothek lässt sich ein erneuter Ausführungsversuch wagen:

tamhan@tamhan-VirtualBox:~/crystalspace/tamswebapp$ crystal run src/tamswebapp.cr 
2022-11-08T04:17:14.363019Z   INFO - athena.framework: Server has started and 
is listening at http://0.0.0.0:3000

Da Ports kleiner als 1024 unter Linux privilegiert sind, entscheidet sich das Athena-Framework eigenständig für einen anderen Port.

Beim Testen der Applikation erweist sich der Browser Firefox mitunter als etwas widerspenstig, weil er bei Eingabe von localhost:3000/ automatisch den HTTPS-Präfix angefügt (s. Abb. 7).

Athena unterstützt HTTPS nicht (Abb. 7).

Nach dem Editieren der Adresszeile ergibt sich das in Abbildung 8 gezeigte Ergebnis. Es informiert darüber, dass Firefox die Informationen erfolgreich aus der Applikation entnehmen konnte.

Die Athena-Framework-Applikation liefert die geforderten Informationen aus (Abb. 8).

Die ersten Experimente mit Crystal zeigen, dass die objektorientierte Programmiersprache trotz diverser innovativer Erweiterungen nicht übermäßig mit den etablierten Konventionen bricht. Sie spielt aber beispielsweise mit ihrer Möglichkeit, Applikationen direkt in Maschinencode zu kompilieren, Vorteile gegenüber Sprachen wie Java aus. Auf die umfassende Flexibilität wie sie zur Laufzeit interpretierte Sprachen bieten, müssen Crystal-Entwicklerinnen und -Entwickler hingegen verzichten. Dennoch punktet die Sprache auch gegenüber Präprozessor-Lösungen, die bei den mit C verwandten Programmiersprachen weit verbreitet sind.

In einem weiteren zu Crystal geplanten Folgeartikel soll die Interaktion mit C-Code näher beleuchtet und ein Ausblick auf die Metaprogrammierung mit selbst modifiziertem beziehungsweise selbst generiertem Code gegeben werden.

Tam Hanna ist Autor, Trainer und Berater mit den Schwerpunkten Webentwicklung und Webtechnologien. Er lebt in Ungarn.

(map [10])


URL dieses Artikels:
https://www.heise.de/-7454848

Links in diesem Artikel:
[1] https://www.heise.de/news/Visual-Basic-6-0-noch-weit-verbreitet-199799.html
[2] https://www.heise.de/news/Programmiersprache-Crystal-erfolgreich-auf-Version-1-0-gezuechtet-5995646.html
[3] https://github.com/crystal-lang/crystal
[4] https://crystal-lang.org/install/
[5] https://crystal-lang.org/install/on_ubuntu/
[6] https://github.com/elbywan/crystalline#pre-built-binaries
[7] https://crystal-lang.org/
[8] https://crystal-lang.org/reference/1.6/
[9] https://athenaframework.org/getting_started/
[10] mailto:map@ix.de