Sonntag, 2. März 2008

Sollten wir nicht etwas dynamischer sein?

Sogenannte "dynamische Sprache" sind das heiße Thema in diesen Tagen. Insbesondere Skriptsprachen wie Ruby und Python finden großes Interesse. Neuerdings lassen sie sich gut mit der Java-Welt kombinieren, wo es JRuby, Jython und speziell nur für die JVM entwickelt Groovy gibt. Ähnliches gilt für die dotnet-Welt, wo es z.B. IronPython gibt.

Die Ideen dieser Sprachen gehen auf Lisp, Smalltalk und teilweise auf funktionale Sprachen zurück (Lisp und Smalltalk haben funktionale Elemente). Somit handelt es sich hier nicht um etwas neues. Die erste Lisp-Spezifikation stammt aus meinem Geburtsjahr.

Der Begriff "dynamisch" ist nicht sonderlich scharf definiert. Eine der wichtigsten Eigenschaften dieser Sprachen ist, daß das Programm auch als Daten aufgefaßt wird, die zur Laufzeit erweitert oder geändert werden können. Daneben haben die meisten dynamischen Sprachen ein wesentlich einfacheres Typsystem.

Ich will hier heute auf einige Aspekte eingehen, die für oder auch gegen die Verwendung dieser Sprachen sprechen.

Performance

Die erste Frage, die immer von skeptischen Zeitgenossen kommt, wenn in der Softwareentwicklung ein neuer Trend aufkommt, ist die nach der Performance. Diese Frage läßt sich leicht stellen, ohne von der Sache sonderlich viel zu verstehen. Sie ist aber so absolut gestellt äußerst naiv. Nicht, daß Performance nie eine Rolle spielt und daß die Rechner bis zur Inbetriebnahme des Softwaresystems bereits so viel schneller sein würden, daß alle Performanceprobleme dadurch ausgeglichen würden. Aber es kommt darauf an, was man entwickeln will. Neulich las ich irgendwo, Ruby wäre 60 mal langsamer als Java. Aber bei welchem Problem? Niemand wird wohl ernsthaft auf die Idee kommen, ein Fast Fourier Transformation in Ruby zu implementieren. Für große numerische Probleme ist wohl immer noch die in Fortran geschriebene CERN-Bibliothek die beste Lösung.

Häufig sind die Zeiten, die im Programm verbracht werden, vernachläßigbar gegenüber Zeiten, die für Datenbankzugriffe, Netzwerkoperationen etc. gebraucht werden. Dann ist auch mit einer weniger effizienten Sprache ein genauso zufriedenstellendes Antwortzeitverhalten hinzubekommen. Allerdings kommt ein Aspekt hinzu, wenn sehr viele parallele Zugriffe erfolgen. Wenn die verwendete Sprache mehr CPU-Zyklen für dieselbe Aufgabe braucht, kann ich weniger Anfragen auf einer Maschine gleichzeitig verarbeiten. Ich muß dann mehr Hardware planen. Die zusätzlichen Hardwarekosten sollten aber kaum eine Rolle spielen. Eher ist schon die benötigte Energie ein Kostenfaktor.

Im übrigen wird der Support von dynamischen Sprachen durch die JVM bzw. CLR zunehmend besser. Dadurch werden die Sprachen auch in diesen Umgebungen immer effizienter.

Typsicherheit

Ein gern gebrachtes Argument von Javafans. Durch die Verwendung einer stark typisierten Sprache werden angeblich viele Fehler bereits vom Compiler gefunden. Leider sieht die Wirklichkeit nicht ganz so rosig aus. Wie häufig werden Downcasts durchgeführt, die vom Compiler nicht überprüft werden und zur Laufzeit dann zu einer InvalidCastException führen. Durch die konsequente Verwendung von Generics kann man das Problem zwar deutlich entschärfen, ganz lösen wird man es jedoch nicht. Allerdings bezahlen wir für diese löchrige Sicherheit noch einen ziemlich hohen Preis, der sich in Entwurfsmustern zeigt, die nur dazu da sind, die fehlende Flexibilität wiederherzustellen. Genaueres zu dieser Problematik ein andermal.

Meiner Erfahrung nach ist ein größeres Fehlerpotential in Ruby oder Python der Umstand, daß Variablen nicht explizit deklariert werden, sondern bei der ersten Verwendung entstehen. So führen Tippfehler zu hartneckigen Fehlern.

Refactoring

Ein neuerdings häufig aufgebrachtes Argument ist das Fehlen guter Refactoring-Werkzeuge für dynamische Sprachen. Dabei wird natürlich immer vergessen, daß das erste derartige Werkzeug der Refactoring Browser für Smalltalk war. Smalltalk ist nun definitiv nicht typisiert. Bei einigen Refactorings erscheint der notwendige Aufwand höher für eine nichttypisierte Sprache. Andere sollten aber unproblematisch oder sogar einfacher sein. "Extract Method" dürfte genauso funktionieren. Umbenennen einer Klasse auch, wobei wesentlich weniger Referenzen zu ändern sind, da die Instanzen nicht explizit mit Typ deklariert werden müssen. Interessanter ist das Umbenennen einer Methode. Da in einer nichttypisierten Sprache Methoden mit einem bestimmten Namen immer akzeptiert werden, egal von welchem Typ der Aufrufer ist, sollten wohl vernünftigerweise auch alle gleichnamigen Methoden in anderen Klassen geändert werden, wenn das Programm nachher noch genauso funktionieren soll wie vorher.

Es ist also damit zu rechnen, daß die Unterstützung durch Refactoring-Werkzeuge in der Zukunft für dynamische Sprachen genauso gut sein wird wie derzeit für Java. Ansätze finden sich z.B. schon in IntelliJs Unterstützung von Ruby for Rails.

Was die Sicherheit betrifft, gibt es kein 100% sicheres Refactoring. Auch im Java-Fall kann man über in Konfigurationsdateien verborgenen Referenzen stolpern oder die Nutzung von Reflection.

Reflection

Manchmal trifft man auf Code, der in Java oder in C# geschrieben ist und der einen extensiven Gebrauch von Reflection macht. Meist geschieht das, um dynamische Eigenschaften zu emulieren. Dabei gehen in der Regel alle Vorteile von typisierten Sprachen endgültig verloren. Mit Reflection kann ich auch mit Java/C# Ducktyping realisieren, d.h. auf Verdacht eine Methode auf einem mir unbekannten Objekt aufrufen und wenn kein Fehler auftritt davon ausgehen, daß es wohl den erwarteten Typ besitzt.

Derartige Programme sind sicher nicht effizienter als äquivalente in dynamischen Sprachen geschriebenen. Hier werden die entsprechenden Funktionen ad hoc neu erfunden, während sie in dafür gemachten Sprachen optimiert sind. Der wichtigste Punkt aber ist, daß derartige Programme meist extrem schlecht lesbar und damit wartbar sind.

Wenn man meint, eine solche Flexibilität zu benötigen, so sollte man doch ernsthaft darüber nachdenken, gleich das dafür gemachte Werkzeug zu verwenden.

Leute

Das größte Problem dürfte derzeit sein, geeignete Entwickler zu finden, die an das hinter den dynamischen Sprachen stehende gedankliche Modell gewohnt sind. Gerade bei kleineren Teams ist es sicher auch schwierig, verschiedene Sprachen zu integrieren, da dann das Wissen über einzelne Elemente nur bei sehr wenigen Mitarbeitern liegt.


Keine Kommentare: