Scala - pragmatische Kombination aus funktionaler und objektorientierter Programmierung

Sprachen  –  Kommentare

Scala ist eine elegante, ausdrucksstarke Programmiersprache, die sich in letzter Zeit zunehmender Beliebtheit und Verbreitung erfreut. Sie lässt sich gut mit Java und der .NET-Plattform integrieren und ist als "General Purpose Language" für alle Aufgaben geeignet, für die man sonst Java oder C# verwendet.

Hintergrund und Rahmenbedingungen

Scala wird von Martin Odersky und seiner Arbeitsgruppe an der Ecole Polytechnique Fédérale de Lausanne entwickelt und gepflegt. Man merkt der Sprache deutlich an, dass Leute sie entwerfen, die einen reichen Erfahrungsschatz an Programmiersprachen besitzen. Die Sprache besteht im Kern aus relativ wenigen, klaren Konzepten, die elegant zusammenspielen und einen "runden" Eindruck hinterlassen. Die erste Release kam 2003 heraus, die zweite, überarbeitete Version 2006, die aktuelle ist 2.7.3. Scala ist gut dokumentiert, und man findet im Internet eine Reihe guter Quellen [1][2][3].

Die Werkzeugpalette um Scala herum – Compiler, Interpreter, IDE-Plug-ins – ist so weit ausgereift, dass die Sprache für den rauen Alltag in "echten" Projekten geeignet ist. Scala wird in Bytecode kompiliert, und es gibt Compiler sowohl für die Java- als auch für die .Net-Plattform. Das Projekt betrachtet die beiden Plattformen als strategisch und unterstützt sie gut, wobei es eine etwas größere Affinität zur Java-Welt gibt.

Eine Stärke von Scala ist, dass es eine explizite Sprachspezifikation gibt – damit hebt es sich von vielen anderen Sprachen wie Groovy ab. Zu Scala gehört eine reiche Auswahl an Standardbibliotheken, die neben Collections unter anderem ein mächtiges auf einem Actor-Modell aufsetzendes Concurrency-Framework im Erlang-Stil umfassen. Darüber hinaus können Scala-Programme alle Bibliotheken und Frameworks der jeweiligen Plattform (Java oder .Net) verwenden.

Eine besondere Erwähnung verdient das Lift-Framework, das auf Scala aufsetzt. Es ist ein robustes, performantes Web-Framework, das den Umfang und die Ausdrucksstärke von Rails mit statischen Typchecks, nahtloser Integration mit Java-Bibliotheken und der Performance von Java verbindet.

Ein erster Überblick

Genug der Vorrede – zu den Grundideen der Sprache: Scala ist funktional, objektorientiert, erweiterbar und stark typisiert:

Funktional: In Scala sind Funktionen vollwertige Sprachelemente, man kann sie in Variablen ablegen oder als Parameter übergeben. Es gibt Closures (anonyme Funktionen, die sich den Kontext ihrer Definition merken), und die Funktionen lassen sich partiell evaluieren. In Scala gibt es keine Statements, sondern alles ist eine Expression, hat also einen Typ und einen Wert. Das ist eine konzeptionelle Vereinfachung gegenüber imperativen Sprachen wie Java oder C#. Seiteneffektfreies Programmieren ist in Scala einfach und nachvollziehbar, es gibt zum Beispiel seiteneffektfreie Collection-Klassen. Das soll man aber nicht als dogmatisch betrachten, denn man kann pragmatisch mit veränderlichen Variablen arbeiten, wenn das gewünscht ist.

Objektorientiert: In Scala ist alles ein Objekt. Man kann beispielsweise auf der Zahl 1 Methoden aufrufen, und der fragile Mechanismus des Autoboxing/Unboxing entfällt. Auch Arrays sind richtige Objekte ohne Sonderbehandlung in der Sprache. Unter der Oberfläche lassen sich Primitives und Arrays – wo umsetzbar – auf die effizienten Konstrukte der jeweiligen Laufzeitumgebung abbilden, sodass dieses einheitliche Programmiermodell nur geringe Performanceeinbußen mitbringt.

Erweiterbar: Man kann in Scala Bibliotheken schreiben, die bei der Verwendung fast wie "eingebaute" Sprachfeatures daherkommen. So gibt es die Regel, dass sich eine Methode mit genau einem Parameter in Infix-Notation ohne Punkt und Klammern aufrufen lässt. Ein gutes Beispiel ist die Methode to, die auf Int definiert ist, ihn als Parameter erwartet und einen Iterator zurückliefert. Damit kann man eine Schleife über einen Zahlenbereich laufen lassen, ohne dass es dafür Spezialsyntax geben müsste:

for (i <- 1 to 10)
println (i)

Man könnte stattdessen 1.to (10) schreiben – to wird hier als normale Methode aufgerufen, inklusive vorangestelltem Punkt –, aber die Infix-Notation liest sich besser. Außerdem gibt es kaum Restriktionen, welche Zeichen in Methodennamen zu verwenden sind. "+" oder "*" sind in Scala ganz normale Methodennamen, und man kann für eigene Datentypen Rechenoperationen definieren, die genau wie die von Int oder Double aussehen.

Stark typisiert: Jede Variable hat in Scala einen genau definierten Typ, den der Compiler ermittelt – genau wie in Java. Wenn man eine Operation aufruft, die für diesen Typ nicht definiert ist, beschwert sich der Compiler. Scala erkennt allerdings den Typ einer Variable aus dem Wert, der sie initialisiert ("Type Inference"). Das Schlüsselwort var legt dabei eine Variable fest:

var x = List (1, 3, 99)  // x hat den Typ List[Int]
x = "Hallo" // Compile-Fehler
x = List ("a", "b") // auch Compile-Fehler

Scala erkennt dadurch beim Kompilieren beziehungsweise im Editor der IDE eine Reihe von Fehlern, ohne dass man die Typen redundant angeben müsste wie in Java.

Das Angeben der Rückgabetypen von Methoden ist in Scala optional, zumindest wenn die Methoden nicht rekursiv sind. Das Schlüsselwort def definiert eine Methode, und die Typen der Parameter stehen in Scala durch einen Doppelpunkt getrennt hinter den Variablen:

def plus (a: Int, b: Int, c: Int) = a + b + c

Diese Code-Schnipsel zeigen, dass sich Scala-Code ziemlich kompakt schreiben lässt. Ein Semikolon am Zeilenende ist optional, und Methodendefinitionen brauchen keine geschweiften Klammern, wenn sie aus einer einzigen Expression bestehen. Der Wert der letzten Expression einer Methode lässt sich ohne explizites "return" zurückliefern. Dadurch gestaltet sich die Definition einer kleinen Hilfsmethode ähnlich kompakt wie das Einführen einer Hilfsvariable. Die Analogie zwischen Variablen- und Methodendefinitionen zeigt sich auch in der Syntax: Die Implementierung wird einer Methode mit einem Gleichheitszeichen "zugewiesen", genau wie der Wert einer Variable.