Runterskaliert

Scala ist im Moment sicher eine der vielversprechendsten Sprachen, die zurzeit um die Gunst der Entwickler buhlen. Bringt Scala doch beispielsweise einige interessante funktionale Aspekte in die imperative Programmierung. Aber leider hat die Sprache einen kleinen, aber nicht zu vernachlässigenden Haken.

vorlesen Druckansicht 17 Kommentare lesen
Lesezeit: 8 Min.
Von
  • Michael Wiedeking

Ich hatte eigentlich relativ früh von Scala gehört. Insbesondere war mir die Gruppe um Martin Odersky wegen der Arbeit an dem Pizza-Compiler bekannt, dessen Generics die in Java maßgeblich beeinflusst hat. Aber beschäftigt habe ich mich mit Scala erst, nachdem ich einen Vortrag von Martin Odersky auf irgendeiner Konferenz gehört hatte und ich zufällig zum gleichen Zeitpunkt ein kleines Programmierproblem lösen wollte.

Bei Scala gefällt mir der Ansatz, dass wirklich ausnahmslos alles ein Objekt ist, wie schön einige der syntaktischen Probleme gelöst werden, wie elegant einige funktionale Features untergebracht sind und – nicht zuletzt – dass ich die komplette Java-Bibliothek zur Verfügung habe. Darüber hinaus würden bei geeigneter Vorsicht bezüglich der Verwendung von Bibliotheken die Scala-Programme auch unter .NET laufen – ein Punkt, der für mich eine zunehmende Bedeutung gewinnt.

Beispielhaft sei hier nur das Operator Overloading erwähnt. Es hatte mich schon bei C++ begeistert, da ich schon immer fand, dass

ab + c

deutlich einfacher als

a.mul(b).add(c)

zu lesen ist. Ein besonders schöner Aspekt ist in diesem Zusammenhang, dass Scala mir erlaubt, beliebige Unicode-Zeichen als Operatoren zu definieren. So konnte ich beispielsweise beim Herumexperimentieren einen regulären Ausdruck der folgenden Art schaffen:

NUMBER = LPAR ∙ (SP↯) ∙ (ALPHANUM ↦ 0) ∙ (SP↯) ∙ RPAR ∙ (SUBNUM ↦ 1)

Mit diesem Ausdruck werden Nummern bezogen auf eine beliebige Basis beschrieben. (10100011)2 oder (CafeBabe)16 sind Beispiele für diese Zahlen (NUMBER): Eine nicht leere Zeichenfolge von Buchstaben und Ziffern (ALPHANUM) eingerahmt in eine rechte und linke Klammer (LPAR und RPAR).

Besonders ist hier zum einen der Blitz (↯), der Ausdrücke – hier Leerzeichen (SP) – so markiert, dass sie dort nur geduldet werden. Im Gegensatz zum Fragezeichen (?), mit dem man Ausdrücke als optional markiert, wird hier eine Warnung ausgegeben. Der Gegenpart davon ist das Ausrufungszeichen (!), mit der man das Fehlen eines Ausdrucks dulden würde. Beispielsweise kann man mit

COMMA ∙ (SP!) 

erreichen, dass in einer Komma separierten Liste jeweils hinter dem Komma ein Leerzeichen kommen muss – und wenn es fehlt, gibt es wieder nur eine Warnung.

Zum anderen besagt das Abbildungszeichen (↦) mit der darauf folgenden 0 oder 1, dass die alphanumerische Zahlenfolge über den Index 0 und die Basis über den Index 1 zu erhalten ist. Damit entfällt das lästige Zählen von Klammern, das bei vielen Bibliotheken für reguläre Ausdrücke nötig ist, die die Schachtelungstiefe als Indizes für diese Zuordnung nutzen.

Natürlich kann man mit überladenen Operatoren auch "Unfug" machen, wie das C++ mit dem Überladen der Shift-Operatoren "<<" und ">>" bei der Ein- und Ausgabe demonstriert hat. Ein Grund für diese suboptimale Wahl der Operatoren ist natürlich im Wesentlichen die beschränkte Anzahl der zur Verfügung stehenden Symbole. Das führt im schlechtesten Fall auch zu einem Überschreiben mit einer anderen Semantik.

Beispielsweise wird in C++ bei normaler Verwendung von "f() && g()" der rechte Ausdruck nur dann ausgewertet, wenn der erste Ausdruck true ist. Überlädt man den Operator, so werden immer beide, der linke und der rechte Ausdruck ausgewertet, bevor die Operator-Funktion aufgerufen wird, was ohne Kenntnis der genauen Definition eventuell für Fehlinterpretationen sorgen kann.

So habe ich also Scala schätzen und nutzen gelernt, habe mich aber trotzdem gegen einen professionellen Einsatz von Scala entscheiden müssen. Das ist insofern bedauerlich, weil Scala einen deutlich höheren Wiederverwendungsgrad hat als beispielsweise Java. Auch in größeren Projekten können durch ein deutlich ausgefeilteres Zugriffssystem viel besser Abhängigkeiten beschrieben werden und missbräuchliche Zugriffe unterbunden werden. Alles in allem ist Scala beispielsweise gegenüber Java für viele Projektarten die bessere Wahl, insbesondere auch, weil die Performanz von Scala der von Java in der Regel in nichts nachsteht.

Der Grund ist ein ganz anderer: Es ist die Entwicklungsumgebung. Ich gehöre eigentlich zu denen, die, bis es nicht mehr anders ging, mit dem "vi", "emacs" oder vergleichbaren Editoren gearbeitet haben. Und eine Zeit lang war das auch vollkommen ausreichend. Allerdings hatte sich das schlagartig mit dem Einzug der Refactoring-Fähigkeiten moderner IDEs geändert.

In der Anfangszeit von Java hatten wir beispielsweise ein Tool, mit dem man Pakete umbenennen konnte. Ein anderes Tool konnte speziell annotierte Ausdrücke entfernen oder umschreiben. Wir hatten sogar Möglichkeiten, als die Projekte noch mit "make" zusammengebaut wurden, einen Präprozessor über die Java-Quellen laufen zu lassen.

Mit den IDEs, spätestens mit dem Erscheinen von Eclipse, wurden alle diese Tools obsolet. Irgendwann kam "ant". Und schließlich konnte man einfach ein Projekt aufmachen, Klassen anlegen und die nach Lust und Laune durch Refactoring verbessern. So konnte beispielsweise ein Name gedankenlos vergeben werden, weil man ihn, sobald sich ein besserer gefunden hat, genauso einfach ändern konnte.

Diese Änderungsmöglichkeiten, die Tatsache, dass ich mit jedem Speichern eine (potenziell) lauffähige Applikation zur Verfügung habe, und die einfache Handhabbarkeit von Projekten sind also von unschätzbarem Wert. All das steht mir leider mit Scala nicht mehr zur Verfügung. Weder das NetBeans- noch das Eclipse-Plug-in leisten hier ausreichend zufrieden stellende Arbeit. Und damit ist ein Einsatz von Scala für mich unmöglich geworden.

Nachdem ich die ersten Gehversuche hinter mir hatte und das Eclipse-Plug-in als etwas besser als das NetBeans-Plug-in bewertet hatte, habe ich mich nur noch mit jenem beschäftigt. Kleine Projekte (die zum "anfixen") funktionieren ganz gut und werfen insbesondere auf Scala ein gutes Licht.

Mein kleines Projekt ist inzwischen auf knapp 90 Scala-Dateien mit etwa 120 Klassen angewachsen. Dieses verhältnismäßig kleine Projekt wird nicht mehr ordentlich übersetzt. Die Änderung einer Klasse kann dazu führen, dass man gelegentlich 50 Dateien öffnen und trivial ändern muss, damit diese korrekt übersetzt werden – ein "build all" gibt es nicht. Besonders fatal ist dabei, dass Quellen übersetzbar sind, obwohl sie es eigentlich nicht sind.

Ja, natürlich könnte ich wieder hergehen und mir die passenden Tools dafür schreiben. Aber das will ich einfach nicht (mehr). Heute erwarte ich tatsächlich von einer Programmiersprache, dass deren Entwicklungsumgebung einen bestimmten Umfang bietet. Das Mindeste dabei ist das Anzeigen der Dokumentation, das Vorschlagen von Bibliotheksfunktionen, das Organisieren von Imports, das Umbenennen von Elementen und das Verschieben von Klassen und Paketen. All diese Funktionen funktionieren beim Scala-Plug-in nur sporadisch oder in letzter Zeit gar nicht mehr. Mit zunehmenden Problemen bin ich zwar auf immer neuere Versionen umgestiegen, die aber auch keine Abhilfe schaffen konnten.

Über den Debugger für Scala kann ich leider nichts sagen, da ich ihn noch nicht verwendet habe (weil ich die Verwendung desselben in meinen eigenen Projekten für ein Zeichen meines Versagens halten würde). Im Zweifelsfall würde es da aber auch der Java-Debugger tun. Für jemanden, der viel mit einem solchen Werkzeug arbeitet, wäre hier eine eingehende Untersuchung dringend zu empfehlen.

So habe ich mein kleines Programm schließlich wieder nach Java übertragen und mich dabei gescheit über den Aufwand geärgert, den ich betreiben musste, um beispielsweise Closures in Java-Code zu übersetzen – das war in Scala deutlich einfacher. Besonders bedauerlich ist aber, das mir kein Pattern Matching mehr zur Verfügung steht, das ich für die Transformation von Bäumen gut hätte gebrauchen können. So muss ich wieder mit deutlich mehr Aufwand "von Hand" machen, was mir Scala quasi nebenbei geschenkt hat.

Dafür konnte ich schon wieder munter Klassen und Methoden umbenennen. Für meine regulären Ausdrücke – und an dieser Stelle sei erwähnt, dass ich dies am wenigsten vermisse – muss ich wieder wie folgt schreiben:

NUMBER = LPAR.cat(SP.forbid()).cat(ALPHANUM.at(0)).cat(SP.forbid())
.cat(RPAR).cat(SUBNUM.at(1));

Und davon kann man jetzt halten was man will. Auf jeden Fall muss ich hier – wie oben schon angedeutet – deutlich sorgfältiger prüfen, was ich denn eigentlich mache.

Nichtsdestoweniger werde ich die Entwicklungsumgebungen von Scala im Auge behalten. Und sobald sie meinen Ansprüchen genügen, werde ich nie wieder in Java programmieren müssen. ()