Wie man eine Programmiersprache auswählt

Florian Weimer

Nach ein paar unangenehmen Überraschungen, die ich mit einigen Programmiersprachen und ihren Implementierungen erlebte, versuche ich an dieser Stelle, einen Katalog an Minimalanforderungen aufzustellen.

Hauptkriterien

Die nächsten Kriterien beziehen sich auf die Implementierung. Insbesondere soll durch sie ausgedrückt werden, daß man sich als Nutzer der Programmiersprache vor Compilerbau und verwandten Tätigkeiten drücken kann. Interessenten der selbstgeschriebenen Anwendung sollten außerdem auf eine aktuelle Implementierung zurückgreifen können (die vielleicht gar ihr Software-Distributor bereitstellte), ohne erst eine Reihe von Spezialpatches einspielen zu müssen.

Sekundäre Kriterien

Diese Kriterien sind zwar ebenfalls wichtig, können aber durch selbstgeschriebene Bibliotheken realisiert werden (und notfalls über Koprozesse).

Andere Dinge sind ebenfalls wünschenswert, lassen sich aber nicht so einfach nachimplementieren:

Ergebnisse

Im Moment gibt es keine Programmiersprache, die alle Kriterien erfüllt, insbesondere wenn auf weitgehende Typprüfung zur Kompilierzeit Wert gelegt wird.

C++ kommt den meisten Anforderungen sehr nahe (insbesondere, wenn bei Bedarf der Boehm-Demers-Weiser-Kollektor verwendet wird), aber versagt beim Schutz der eigenen Abstraktionen. Eine alternative Implementierung der Standardbibliothek, die möglichst viele Prüfungen vornimmt und zusätzlich robuste Puffer-Implementierungen bereitstellt, könnte allerdings diese Problematik lindern. Der I/O-Teil der C++-Standardbibliothek ist andererseits auch nicht besonders geeignet für Binär-I/O auf POSIX-Deskriptoren. Dafür gibt es eine offene, sehr gut gepflegte Implementierung, und auch sehr viele Debugging-Werkzeuge.

Für C müßten Anforderungen wie Ausnahmebehandlung und OOP durch Konventionen nachgerüstet werden. Das geht im Prinzip, wie der Linux-Kernel und die Apache Portable Runtime (APR) zeigen, ist aber aufwendig. Echte Destruktoren sind aber nicht zu ersetzen (bzw. nur mit sehr viel Präprozessor-Magie, die wiederum Probleme aufwirft).

Ada kommt, was die Sprache angeht, den Anforderungen noch näher als C++. Aber es gibt auf der Implementierungsseite erhebliche Einschränkungen. GNAT, die freie Ada-Implementierung, ist zwar inzwischen Teil von GCC, schaffte es dort aber nicht, zu den primären Sprachen C und C++ aufzuschließen. Dies liegt nicht zuletzt daran, daß die Entwicklung von GNAT hauptsächlich im stillen Kämmerlein bei AdaCore abläuft (unter Verwendung der vielgerühmten proprietären Testsuite), und daß die Entwickler bei AdaCore das Ada-Front-End nicht so zeitnah wie die anderen Maintainer an GCC-Infrastrukturänderungen anpassen. Ferner werden Bugs im Ada-Compiler durch die Maintainer nur in der neuesten Entwicklerversion behoben, eine Pflege älterer Versionen findet in der Regel nicht statt. Die letzte Veröffentlichung einer vollständig getesteten, offiziellen Testversion seitens AdaCore liegt zudem fast drei Jahre zurück und basiert auf der historischen GCC-Version 2.8.1., und durch einige Ada-unabhängigen Neuerungen (NPTL, AMD64) ist ihr Einsatz nicht immer möglich. (Wenn man sich den Luxus eines AdaCore-Supportvertrages leisten kann, fallen diese ganzen Probleme sehr wahrscheinlich weg.) Aufgrund einiger Implementierungsdetails von GNAT ist das Nachrüsten von Garbage Collection mit dem Boehm-Kollektor zudem nicht so einfach möglich.

Java hat ebenfalls das Problem, daß echte Destruktoren fehlen. Die Kosten für die Erzeugung eines Java-Prozesses sind selbst bei Compilern wie GCJ hoch, da die Sprachnorm eine dynamische Initialisierung nahezu erzwingt. Die Qualität der freien Implementierung kann, auch was den Compiler selbst angeht, nicht mit C und C++ mithalten. (Die Qualität der Reimplementierung der Sun-Bibliotheken ist für unsere Zwecke eher zweitrangig.) Wirklich unangenehm ist das Fehlen von vorzeichenlosen Ganzzahltypen, was Bitmanipulationen häufig noch weniger transparent macht, als sie prinzipienbedingt schon sind.

C# versucht die erwähnten Java-Probleme zu korrigieren: C# bietet vorzeichenlose Ganzzahltypen und eine recht gute Annäherung an das, was in C++ per RAII gemacht wird (über das using-Konstrukt). Mit Mono existiert zwar eine freie Implementierung, aber die tatsächliche Unabhängigkeit von Microsoft und den essentiellen Patenten ist derzeit noch etwas unklar. Microsoft scheint offenbar auch die seit einigen (Sun-)Versionen ziemlich bedingungslose Rückwärtskompatibilität vom Java zu diesem Zeitpunkt nicht nachahmen zu wollen, was einerseits den Vorteil hat, daß sich Irrwege entsorgen lassen, andererseits aber natürlich auch dazu führt, daß Anwendungen beim Wechsel der Version der C#-Implementierung angepaßt werden müssen (das Grauen eines jeden PHP-Entwicklers).

Bei den Sprachen, die auf Laufzeit-Typprüfung setzen, ist Python ein guter Kandidat. Perl ist ebenfalls brauchbar, allerdings ist der Einsatz von Ausnahmen dort eher unüblich, und die Ladezeit von Perl-Anwendungen, die viele Perl-Module verwenden, ist unangenehm hoch. Ruby dürfte inzwischen ebenfalls die kritische Masse erreicht haben, die eine Weiterentwicklung der Implementierung sicherstellt. Auf der anderen Seite ist die Verarbeitung einzelner Bytes mit diesen Sprachen zwar prinzipiell möglich, aber nicht besonders effizient.

Im Bereich der funktionalen Sprachen ist der Markt ziemlich fragmentiert, da es eine Zeit gab, als Implementierungen lohnende Forschungsprojekte waren. Viele Implementierungen verfügen aber nicht mehr über die anfängliche Zahl von Entwicklern, was die Wartung und Anpassung an neue Entwicklungen (z.B. AMD64 und 64-Bit-Systeme allgemein) schwierig macht. Es gibt sehr vielen Fällen auch erhebliche Hindernisse bei der Integration der Standard-Bibliotheken in eine UNIX-Umgebung (z.B. sind Trivialprogramme wie cat nicht mit I/O-Bordmitteln zu implementieren). In den meisten Fällen muß man daher als Anwendungsprogrammierer durchaus damit rechnen, auch bei der Sprachimplementierung selbst Nachhilfe leisten zu müssen, mit all den einhergehenden Nachteilen.

In diesem Bereich scheint aber GHC (eine Haskell-Implementierung) an der Schwelle zum Überschreiten der kritischen Masse an Entwicklern zu stehen. Haskell bzw. GHC weisen auch nicht die Nachteile älterer Implementierungen funktionaler Sprachen auf (z.B. die Tendenz, Anwendungen in Form von Megabyte-großen Images auszuliefern).

Revisions


Florian Weimer
Home Blog (DE) Blog (EN) Impressum RSS Feeds