Objective Caml

Florian Weimer

Objective Caml, ein Vertreter aus der ML-Sprachfamilie, scheint wider erwarten eine nutzbare Programmiersprache (und Implementierung) zu sein.

Meine Vorgeschichte: Standard ML

Unter Wie man eine Programmiersprache auswählt beschrieb ich einige Kriterien für die Auswahl einer Programmiersprache. Als ich diesen Text schrieb, zog ich Objective Caml <http://caml.inria.fr/ocaml/index.en.html> nicht in die nähere Wahl. Ich begann damals erst, mich mit den ML-Sprachen auseinanderzusetzen – zu jenem Zeitpunkt kannte ich nur ein bißchen Haskell, und “The Definition of Standard ML” bestellte ich erst Mitte Juni 2005. Von Objective Caml wußte ich bislang nur, daß mldonkey darin geschrieben ist (weswegen es als unwartbar gilt) und daß der Compiler unter einer leidlich entschärften Fassung der “Q Public License” steht (das Laufzeitsystem unterliegt einer nochmals verharmlosten LGPL-Variante).

Nach einigen Spielereien mit Standard ML (hauptsächlich mit MLton <http://mlton.org/>, einer Implementierung, die Programme als ganzes optimiert) fand ich gefallen an den Konzepten: Typprüfung zur Kompilierzeit, Closures, Ausnahmen, Finalisierung und effiziente Bit-Manipulation (bei MLton zumindest). MLton bietet außerdem eine ganz passable Schnittstelle zu in C geschriebenen Bibliotheken an. Die Ressourcenverwaltung (für die man in Sprachen wie C++ zweckmäßigerweise Destruktoren einsetzt) kann man, ähnlich zu Lisp, mit kleinen Wrapper-Funktionen nachbilden, die das gewünschte Objekt erzeugen, eine vom Benutzer der Bibliothek bereitgestellte (i. d. R. namenlose) Funktion aufrufen und das Objekt wieder zerstören (egal wie die Funktion verlassen wird). Einiges von meiner ursprünglichen Liste ist also erfüllt.

Was jedoch fehlt, sind real existierende Anwendungen, die in Standard ML (SML) entwickelt wurden. Sicherlich gibt es einige automatische Beweiser und natürlich die SML-Implementierungen selbst, aber das sind alles Nischenprodukte, die wenig über die Eignung für meine Belange aussagen. Keine der genannten Anwendungen greift zum Beispiel auf Datenbanken zu oder interagiert in nennenswerter Weise mit dem Netz.

Was jedoch ärgerlicher bedeutsamer ist: Die SML-Implementierungen (Standard ML of New Jersey <http://www.smlnj.org/>, besagtes MLton, Alice <http://www.ps.uni-sb.de/alice/>) werden entweder nicht so richtig weiterentwickelt, z. B. zur direkten Erzeugung von nativen Code für IA64 (vielleicht nicht ganz so wichtig) oder AMD64 (schon eher) – oder arbeiten von Vorneherein nur mit Bytecode (wie Alice). Das liegt womöglich daran, daß es für diese Sprachen keinen Markt im herkömmlichen Sinne gibt und daß sich die Entwicklung ganz wesentlich an den eigenen Forschungsbedürfnissen orientiert. Das muß nichts schlechtes sein für die Sprache, kann aber bedeuten, daß der Fokus eher bei innovativen Typsystemen liegt als in der Bereitstellung eines Debuggers.

Damit wären wir auch schon beim nächsten Punkt: Debugging bei funktionalen Sprachen sieht grundsätzlich übel aus. Das gilt für Haskell gleichermaßen wie für Standard ML. Bei Haskell ist das wegen der „faulen“ Auswertung wirklich ein Forschungsprojekt, und nicht nur eine Frage des Programmierens. Typischerweise bieten Implementierungen zwar eine interaktive Konsole an, mit der sich einzelne Funktionen manuell zwecks Fehlersuche testen lassen. Ausgerechnet MLton tut das aber nicht. Da im SML-Lager die C-Schnittstelle nicht normiert ist, kann man sich nicht einfach mit einer anderen Implementierung auf Fehlersuche begeben.

Schweren Herzens kam ich also zu dem Schluß, daß Programmieren in SML zwar Spaß macht und auch praxistaugliche Ergebnisse liefern kann, aber daß es möglicherweise zu denselben Problemen wie bei Ada führt, die mich veranlaßten, den eingangs erwähnten Artikel zu verfassen. Aber immerhin hatte ich mich nach und nach an die Syntax von SML gewöhnt. Insofern war ich offenbar reif für Objective Caml.

Erste Eindrücke zu Objective Caml

Objective Caml <http://caml.inria.fr/ocaml/index.en.html> ist eine Sprache, die sich einst an Prä-Standard-ML orientierte, heute zwar konzeptionell noch immer recht nahe an SML liegt, aber zahlreiche syntaktische Abweichungen aufweist. Die Standardbibliothek (sie heißt bei SML die „Basis“) ist natürlich auch komplett verschieden.

Wenn man die SML-Syntax gewohnt ist, fühlt sich die Objective-Caml-Syntax ein wenig wie Unterschichten-Programmieren an. Objektiv betrachtet ist das natürlich Blödsinn, und im Nachhinein ist auch nur schwer nachvollziehbar, woher das Gefühl kam. Vielleicht ist SML zwar eine Spur eleganter; und wie so üblich, nehmen derartige Gefühle zu, je kleiner die Unterschiede sind. Die Caml-Syntax ist jedenfalls typisch für die ML-Sprachen und somit außerordentlich gewöhnungsbedürftig, falls man zuvor nur C, Lisp oder Python kannte.

Ähnlich wie bei Perl oder PHP steht der Name “Objective Caml” sowohl für die Programmiersprache, als auch die Implementierung. Je nach Blickwinkel kann das ein Vor- oder Nachteil sein (aber das ist ein anderes Thema, das seinen eigenen Artikel wert ist). Anders als bei den gängigen Skriptsprachen gibt es sowohl einen Compiler für die eigene virtuelle Maschine und einen Compiler für nativen Code.

Wie schlägt sich Objective Caml nach den in Wie man eine Programmiersprache auswählt aufgestellten Kriterien? Statische Typprüfung, Closures, Ausnahmen, Byte-Arrays mit effizientem Zugriff gibt es. Die Implementierung wird gewartet (wenn auch nicht vielleicht in dem Umfang, wie z. B. GCC) und halbwegs offen entwickelt (wobei viel Kommunikation natürlich innerhalb von INRIA abläuft). Unison <http://www.cis.upenn.edu/~bcpierce/unison/>, MLDonkey <http://www.nongnu.org/mldonkey/> und Hevea <http://pauillac.inria.fr/~maranget/hevea/> können wohl als echte, in Objective Caml geschriebene Anwendungen gelten. Die C-Schnittstelle ist umfassend und nicht unnötig kompliziert (wobei man C-Module sowohl für die Bytecode-Variante von Objective Caml verwenden kann, als auch für den Native-Code-Compiler). Die Ladezeit für kleinere Anwendungen ist sehr kurz und bei größeren auch unproblematisch. Bei den damals als Hauptkriterien festlegten Merkmalen schaut es also schon ganz gut aus.

Die bei Objective Caml mitgelieferten Bibliotheken machen den Eindruck, als ob sie zumindest im Kernbereich für die UNIX-Systemprogrammierung taugen. Es gibt Thread-Unterstützung, aber innerhalb eines Prozesses kann immer nur ein Thread Objective-Caml-Code ausführen (vergleichbar zum Interpreter-Lock bei Python). Das Linken in C-Anwendungen ist möglicherweise schwierig; ich habe es noch nicht ausprobiert. Objective Caml hat einen sicheren Kern, innerhalb dessen sich die allermeisten Anwendungen implementieren lassen (wenn man einmal die C-Schnittstelle außerachtläßt, die naturgemäß zu “trusted code” führt). Ein Debugger auf Quelltextebene ist vorhanden, sofern nach Bytecode kompiliert wird (er ist unkonventionell, was aber kein Fehler ist – dazu mehr in einem anderen Artikel). Garbage Collection ist verpflichtend, kann also nicht abgeschaltet werden. Bis auf den letzten Punkt und die etwas eingeschränkte Threading-Unterstützung sieht es also auch bei den sekundären Kriterien ganz gut aus.

Trotzdem, ein Zweifel bleibt: Kann eine funktionale Programmiersprache überhaupt geschwindigkeitsmäßig mithalten? Oder müssen die geschwindigkeitskritischen Teile doch wieder in C oder C++ implementiert werden? In diesem Fall wäre es wohl sinnvoller, die C/C++-Komponenten mit einer gängigen Skriptsprache wie Python zusammenzukleben, die mehr Entwickler beherrschen.

Geschwindigkeit

Um es vorwegzunehmen: Objective Caml erzeugt erstaunlich flinken Code, ohne daß große Verrenkungen seitens des Programmierers notwendig wären. Das ist umso überraschender, wenn man bedenkt, daß dem nach gängiger Meinung erhebliche Hindernisse entgegen stehen:

Diese Hindernisse sind offenbar nicht so gravierend für tatsächlichen Objective-Caml-Code, wie man annehmen könnte. Die Tagging-Problematik ist gegenüber Sprachen wie Lisp entschärft, weil z. B. eine „+“-Operation nicht die Tag-Bits zur Laufzeit untersuchen muß, sondern die benötigte Implementierung bereits zur Kompilierzeit ermittelt und deren Adresse direkt ins Kompilat geschrieben werden kann.

Methodenaufrufe spielen eine geringere Rolle als beispielsweise bei Java, weil ja noch Closures existieren. Auch muß man wegen des reichhaltigeren Typsystems in Objective Caml nicht für jede Kleinigkeit eine eigene Klasse einführen.

Monomorphisierung und Defunktorisierung vergrößern die Codegröße teilweise drastisch; möglicherweise frißt die Zunahme an Instruction Cache Misses, die sich daraus ergibt, die Vorteile wieder auf, so daß der Ansatz von Objective Caml, möglichst viel Code über indirekte Funktionsaufrufe zu teilen, in der Praxis zu keinen großen Nachteilen führt.

Der letzte offene Punkt ist die Garbage Collection. Bei Objective Caml kommt ein recht ausgeklügelter Collector zum Einsatz, der zwei Generationen verwendet. Die erste wird mit einem Copying Collector beackert, die zweite verwendet ein inkrementelles Mark-&-Sweep-Verfahren mit gelegentlichem Kompaktifizieren. Das bedeutet, daß die Allokation von kurzlebigen, kleinen Objekten sehr billig ist (vgl. auch Brian Goetz, Urban performance legends, revisited – Allocation is faster than you think, and getting faster <http://www-128.ibm.com/developerworks/java/library/j-jtp09275.html?ca=dgr-lnxw01JavaUrbanLegends>, September 2005 für eine ähnliche Betrachtung). Ersten Tests zufolge verbringen speicherintensive Objective-Caml-Programme auch tatsächlich erfreulich wenig Zeit im Garbage Collector.

Fazit

Alles in allem macht Objective Caml einen vielversprechenden Eindruck. Spaßeshalber versuchte ich auch, die aktuelle Distribution auf einem doch etwas betagteren FreeBSD-4.9-System zu kompilieren, und es traten keine Probleme auf. Da die Liste der Plattformen, die von Objective Caml unterstützt wird, ziemlich lang ist (und ich davon ausgehe, daß sich die Entwicklungsumgebung dort ebenfalls einfach übersetzen läßt), bietet sich mir vielleicht doch noch die Möglichkeit, mich vor C++-Programmierung in größerem Umfang zu drücken.

Mag sein, daß ich das anders, wenn ich ein paar tausend Zeilen Objective-Caml-Code geschrieben habe. Wir werden sehen.

Nachtrag

Die Verwandtschaft mit Objective C (Brad Cox, “Object-oriented Programming; An Evolutionary Approach”, Addison-Wesley) scheint, wie in der ersten Fassung dieses Artikels angedeutet, tatsächlich nur Zufall zu sein. Der maßgebliche Aufsatz zum Objektsystem von Objective Caml scheint “Objective ML: An effective object-oriented extension to ML” von Didier Rémy and Jérôme Vouillon zu sein (online erhältlich über die Bibliographie von Didier Rémy <http://pauillac.inria.fr/~remy/publications.html>). In ihm wird Objective C und Brad Cox' Arbeit aber nicht erwähnt.

Kurios ist auch die Weise, wie ich den Aufsatz über “Objective ML” fand: nicht über eine Suchmaschine, nicht durch Nachfragen auf einer Mailingliste, sondern ganz klassisch im Quellenverzeichnis von Types and Programming Languages <http://www.cis.upenn.edu/~bcpierce/tapl/> von Benjamin C. Pierce.

Revisions


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