Einstieg in Crystal: Kompilierte Sprache mit modernen Konzepten

Seite 2: "Hallo Welt" mit Crystal

Inhaltsverzeichnis

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)

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.

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.