Open Source im professionellen Einsatz
Linux-Magazin 06/2014
© Nudda Chollamark, 123RF

© Nudda Chollamark, 123RF

Programmieren in Ocaml

Treues Lasttier

Die Programmiersprache Ocaml gilt als exotisch. Dabei bietet sie neben manchen Eigenheiten insbesondere für Python-, Java- und C-Programmierer Vertrautes. Der Verfasser des Artikels hat den Code seines Open-Source-Projekts auf Ocaml umgestellt und es nicht bereut.

1023

Großen Wert legt die Programmiersprache Ocaml [1] auf Korrektheit und Performance. Ihr statisches Typensystem beispielsweise sorgt dafür, dass die von anderen Sprachen bekannten Segmentation Faults und Null Pointer Exceptions nicht auftreten.

Da Ocaml [2] sich sowohl im funktionalen als auch im imperativen Stil programmieren lässt, eignet sie sich für Entwickler, die von Python, C oder Java kommen und robustere Software schreiben möchten. Der Autor beispielsweise hat seinen plattformübergreifenden Paketmanager Zero Install [3] im Jahr 2013 nach Ocaml portiert (Abbildung 1). Für das Übertragen der 29000 Zeilen Ausgangscode in Python brauchte er sieben Monate und lernte dabei Ocaml.

Abbildung 1: Der Autor hat seinen freien Paketmanager Zero Install von Python nach Ocaml portiert.

Im Jahr 1996 wurde die Sprache unter dem Namen Objective Caml erstmals veröffentlicht. Sie erweiterte die ältere Programmiersprache Caml um objektorientierte Features. Inzwischen handelt es sich um eine reife, stabile und gut dokumentierte Sprache, deren Implementierung unter den Open-Source-Lizenzen QPL und LGPL steht. Zu den Open-Source-Projekten, die Ocaml einsetzen, gehören der Hypervisor Xen, die Filesharing-Software ML Donkey sowie Coq, ein mathematisches Beweisführungsprogramm.

Die Ocaml-Syntax sieht zunächst ein wenig befremdlich aus. Das folgende Beispiel soll Klärung schaffen (siehe auch den Kasten "Ocaml-Code ausführen"):

let x = 3
let y = 4
let () =
  Printf.printf
    "%d + %d = %d\n"
    x y (x + y)

Ocaml wertet die »let« -Deklarationen der Reihe nach aus und schließt dabei automatisch auf die Datentypen. Ausdrücke, die keinen sinnvollen Rückgabewert liefern, etwa »printf« , geben das leere Tupel »()« zurück, das auch Unit genannt wird und dem »void« in C oder Java ähnelt. Indem der Programmierer das Ergebnis der Unit zuordnet, veranlasst er den Compiler zu prüfen, ob der Ausdruck auch wirklich »()« zurückliefert oder ob der Entwickler vielleicht etwas Wichtiges übersehen hat. »Printf.printf« bezeichnet die Funktion »printf« im Modul »Printf« (Modulnamen beginnen stets mit einem Großbuchstaben). Abbildung 2 zeigt das Ergebnis der Ausführung.

Abbildung 2: Das erste Ocaml-Programm ist schnell ausgeführt.

Ocaml-Code ausführen

Eine Ocaml-Installation bietet neben Compilern für Bytecode und nativen Binärcode auch einen Interpreter. Um ein Listing auszuführen, speichert man den Code in einer Datei, etwa »prog.ml« , und ruft »ocaml prog.ml« auf.

Ohne Argument startet das Kommando »ocaml« eine interaktive Interpreter-Sitzung. In diesem Modus muss der Anwender am Ende des Codes zwei Strichpunkte (»;;« ) eingeben, um ihn auszuführen.

Funktionen

Das Schlüsselwort »let« kommt auch zum Zuge, um Funktionen zu definieren:

let add x y = x + y

Im Unterschied zu C, Python oder Java setzten Ocmal-Programmierer keine Klammern um die Argumente, auch das Schlüsselwort »return« gibt es nicht. Ocaml wertet den Funktionskörper einfach als einen einzigen Ausdruck aus. Sogar ein If-else-Konstrukt ist solch ein Ausdruck, der einen Wert produziert:

let rec fact n =
  if n = 1 then 1
  else n * fact (n - 1)

Definiert ein Entwickler eine rekursive Funktion, wie hier die Berechnung der Fakultät, verwendet er das Schlüsselwort »rec« , damit die Funktion sich selbst aufrufen kann. Ließe er es weg, wäre der Funktionsname »fact« im Körper nicht sichtbar.

Bitte mit Curry

Ocaml-Funktionen nehmen nur ein einziges Argument entgegen – auch wenn es so aussehen kann, als wären es mehrere. Dahinter steckt das so genannte Currying, die partielle Funktionsanwendung (benannt nach dem amerikanischen Mathematiker Haskell Curry). Dabei nimmt die Funktion das erste Argument an und gibt eine neue Funktion zurück, die das nächste Argument verarbeitet. Das führt dazu, dass die folgenden beiden Ausdrücke äquivalent sind:

add x y
(add x) y

Hier wäre »add 5« eine Funktion, die eine Ganzzahl nimmt und dazu 5 addiert:

let add_five = add 5
let six = add_five 1

Viele Ocaml-Programmierer nutzen die teilweise Funktionsanwendung, um Code kürzer und prägnanter zu schreiben. Die folgenden Zeilen sind gleichbedeutend:

let hi x = printf "Hi, %s!\n" x
let hi = printf "Hi, %s!\n"

In beiden Schreibweisen handelt es sich bei »hi« um eine Funktion mit der Signatur »string -> unit« . Das heißt, sie nimmt eine Zeichenkette und gibt das leere Tupel zurück. Als besonders praktisch erweist sich die partielle Anwendung in Schleifen. So gibt man jedes Element einer Liste aus:

List.iter
  (Printf.printf "- %s\n")
  ["foo"; "bar"]

Das erste Argument für »List.iter« ist die Funktion, die auf jedes Listenelement angewandt werden soll. Teilweise Funktionsanwendung erspart es dem Programmierer in diesem Fall, dafür eine neue Funktion zu definieren.

Funktionen mit Currying erlauben noch weitere praktische Kniffe. Dazu gehört der nützliche Pipe-Operator, den man folgendermaßen als Funktion definieren kann: »let (|>) x f = f x« .

Er nimmt das Argument »x« sowie eine Funktion »f« entgegen und wendet die Funktion auf das Argument an. Was sich zunächst trivial lesen mag, erlaubt eine Schreibweise, die den eingefleischten Linuxer in angenehmer Weise an Shell-Pipelines erinnert:

["foo"; "bar"]
|> List.map String.uppercase
|> List.iter
   (Printf.printf "- %s\n")

Die Funktion »List.map« produziert eine neue Liste, indem sie die angegebene Funktion auf alle Listenelemente der Eingabe anwendet. Der obige Code transformiert die Zeichenketten zuerst in Großbuchstaben und gibt sie dann einzeln auf je einer Zeile aus. Dabei interpretiert Ocaml »x |> f |> g« als »(x |> f) |> g« . Der Pipe-Operator ist in neueren Versionen der Sprache bereits eingebaut.

Ocaml setzt die aus anderen Sprachen bekannte Switch-Verzweigung mittels »match« um:

let to_string x =
  match x with
  | 1 -> "one"
  | 2 -> "two"
  | n -> string_of_int n

Dieser Code schreibt die Zahlen eins und zwei als Wörter auf das Terminal, alle höheren zeigt »string_of_int« als Dezimalzahlen an. Links von den Pipe-Symbolen stehen die Muster, die auf die einzelnen möglichen Fälle passen.

Diesen Artikel als PDF kaufen

Express-Kauf als PDF

Umfang: 6 Heftseiten

Preis € 0,99
(inkl. 19% MwSt.)

Linux-Magazin kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

  • Code-Vehikel

    Alternativen zum Programmieren mit C++ gibt es viele. Doch welche davon eignen sich für mehr als eine Spielerei? Dieser Artikel nennt einige praxistaugliche Programmiersprachen und stellt das zu Unrecht wenig bekannte Ocaml näher vor.

     

  • Zero Install Injector 2.6 ganz in OCaml

    Mit Version 2.6 komplettiert das dezentrale Installationssystem Zero Install Injector den Umstieg von Python auf OCaml und bringt weitere Neuerungen.

  • Rumpf-Betriebssysteme

    Im Zeitalter von Container und Cloud verliert das Betriebssystem an Geltung. Fast unverwundbare Rumpfsysteme, die sich nur noch ums Elementare kümmern, kommen da gerade recht und retten den Tag.

  • Ocaml-Vortragsfolien online

    Die Vortragsfolien des Ocaml Users Meeting, das Mitte April in Paris stattfand, sind nun online verfügbar.

  • Cloud-Systeme

    Open-Source-Software spielt in der Cloud zwar die erste Geige, doch als Betriebssystem fehlt manchem Linux die Leichtigkeit, um in die Sphären aufzusteigen. Cloudbetriebssysteme wollen Anwendungen in der Rechenwolke schlank machen, das Linux-Magazin stellt vielversprechende Anwärter vor.

comments powered by Disqus

Ausgabe 10/2017

Digitale Ausgabe: Preis € 6,40
(inkl. 19% MwSt.)

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.