Einstieg in Crystal: Kompilierte Sprache mit modernen Konzepten
Seite 2: "Hallo Welt" mit Crystal
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.
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:
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".
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.
Kontrollstrukturen in Crystal
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
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.