~/ / docs / Grundlagen / 04 · Grundlagen
Module und Signaturen
Wie Module Code strukturieren, was Signaturen versprechen und warum die Trennung von Implementierung und Schnittstelle hier so direkt ist.
OCaml gehört zu den wenigen Sprachen, in denen das Modulsystem nicht nachträglich aufgesetzt, sondern Teil des Sprachkerns ist. Jede .ml-Datei ist ein Modul. Optional dazu kann eine .mli-Datei die Signatur definieren — also festlegen, was nach außen sichtbar ist.
Module aus Dateien
Eine Datei geo.ml mit folgendem Inhalt wird automatisch zum Modul Geo:
(* geo.ml *)
let pi = 3.14159
let area r = pi *. r *. r
let circumference r = 2.0 *. pi *. r
In einer anderen Datei greift man darauf zu, indem man den Modulnamen vor den Bezeichner setzt:
let () =
Printf.printf "Fläche: %f\n" (Geo.area 5.0)
Mit open Geo zieht man alle Bezeichner in den aktuellen Scope. Das ist bequem, sollte aber sparsam genutzt werden — globales open verwischt, woher ein Name kommt.
Signaturen kapseln
Eine Signatur beschreibt das Interface eines Moduls. Die Datei geo.mli daneben gelegt schränkt ein, was Außenstehende sehen:
(* geo.mli *)
val area : float -> float
val circumference : float -> float
Hier fehlt pi — der Wert bleibt damit modul-intern, obwohl er in geo.ml definiert ist. Auf diese Weise schützt OCaml Implementierungsdetails. Wer Geo.pi schreibt, bekommt einen Compile-Fehler.
File "main.ml", line 3, characters 9-15:
Error: Unbound value Geo.pi
Module im Modul
Module lassen sich auch inline definieren — praktisch für lokale Gruppierung:
module Stack = struct
type 'a t = 'a list
let empty = []
let push x s = x :: s
let pop = function
| [] -> None
| x :: xs -> Some (x, xs)
end
'a ist eine Typvariable und macht den Stack über jeden Elementtyp generisch. Verwendet wird er dann ganz natürlich:
let s = Stack.push 1 Stack.empty
let s = Stack.push 2 s
Das passende Signatur-Pendant:
module Stack : sig
type 'a t
val empty : 'a t
val push : 'a -> 'a t -> 'a t
val pop : 'a t -> ('a * 'a t) option
end
Wichtig: type 'a t ohne = macht den Typ abstrakt. Außenstehende können 'a Stack.t benutzen, aber nicht wissen oder ausnutzen, dass er intern eine Liste ist. Das ist klassische Datenkapselung — durchgesetzt vom Typsystem.
Mit Modulen und Signaturen lässt sich Code wirklich entkoppeln. Im nächsten Kapitel geht es um die wichtigsten Datenstrukturen in der täglichen Arbeit: Listen, Records und Varianten.