Laboratorium 3: Wprowadzenie do modułów w Ocamlu

Specyfikacje i implementacje w Ocamlu

Ocaml bardzo silnie wspiera programowanie oparte o kontrakty (ang. Design by contract).
Rzeczywiście, każdy większy program w Ocamlu podzielony jest na moduły, z których każdy składa się ze specyfikacji
(czyli kontraktu, umieszczonego w pliku *.mli) i implementacji (w pliku *.ml).
Przykładowo, plik.mli może wyglądać tak:

 
    type typ
    val wartosc : typ
    val operacja : typ -> typ
    val string_of_typ : typ -> string
 

Odpowiadający mu plik.ml
może wyglądać tak:

 
    let wartosc = 7
 
    let operacyjka x = x + 1
    let operacja x = operacyjka (operacyjka x)
 
    type typ = int
    let string_of_typ = string_of_int
 

Aby sprawdzić, że implementacja spełnia swoją specyfikację, należy najpierw skompilować plik.mli:

 
    ocamlc -c plik.mli
 

O ile nie ma błędu, wyprodukowany zostanie plik.cmi.
Teraz można skompilować plik.ml:

 
    ocamlc -c plik.ml
 

Ta komenda zakończy się powodzeniem, czyli wyprodukowaniem pliku plik.cmo, tylko jeśli wcześniej został
skompilowany plik.mli i implementacja rzeczywiście odpowiada specyfikacji.
Teraz w drugi_plik.ml
można skorzystać z poprzedniego modułu w następujący sposób:

 
    let x = Plik.wartosc
    let y = Plik.operacja x
 
    open Plik
 
    let z = operacja y
    let _ = print_endline (string_of_typ z)
 


Zauważmy, że drugi_plik.ml nie ma pojęcia, że typ i int to to samo, ani że
istnieje coś takiego, jak operacyjka.
Istotnie, nie ma tego w specyfikacji modułu "Plik".


Aby skompilować drugi_plik.ml należy wykonać:

 
    ocamlc -c drugi_plik.ml
 

Otrzymamy plik drugi_plik.cmo.
Aby uzyskać gotowy program wykonywalny (to tzw. linkowanie) należy wykonać:

 
    ocamlc -o test plik.cmo drugi_plik.cmo
 

Teraz można sprawdzić, że działa:

 
    ./test
 

Co zostanie wypisane?

Wszystkie powyższe pliki można pobrać w postaci archiwum zip.

Warto w tym miejscu zapamiętać, że nazwy modułów piszemy w Ocamlu zawsze wielką literą, a nazwy
odpowiadających im plików zwyczajowo pisane są małymi literami.

Interaktywne środowisko i pliki kompilowane

Skompilowanych modułów można także używać wewnątrz interaktywnego środowiska Ocamla (tego
uruchamianego poleceniem ocaml, lub pod emacsem).
Przy pierwszym użyciu nazwy modułu Plik, ładowany jest automatycznie odpowiadający mu plik.cmi,
zawierający informacje o typach komponentów modułu.
Nie jest jednak ładowany kod samego modułu (plik.cmo), dlatego każda próba użycia komponentów
kończy się komunikatem o błędzie:

 
      # Plik.wartosc;;
      Reference to undefined global `Plik'
 

Plik.cmo ładowany jest tylko na wyraźne życzenie użytkownika.
Można do tego celu użyć komendy #load:

 
      # #load "plik.cmo";;
 

Uwaga: pierwszy # (krzyżyk/hash) jest znakiem zachęty środowiska, drugi musimy wprowadzić samemu, podobnie
jak w przypadku komendy #use czy #quit.

Powyższa komenda dołącza skompilowany moduł Plik do środowiska.
Po tej komendzie komponentów modułu Plik można używać za pomocą prefiksu, np.:

 
      Plik.string_of_typ Plik.wartosc;;
 

albo bez prefiksu, po uprzednim wykonaniu komendy open Plik:

 
      open Plik;;
      string_of_typ wartosc;;
 

Można również wydać polecenie załadowania skompilowanego modułu przy starcie środowiska:

 
      ocaml plik.cmo
 

Można też stworzyć własną wersję środowiska z załadowanym plikiem:

 
      ocamlmktop plik.cmo -o moj_toplevel
 

I potem używać jej zamiast standardowego środowiska:

 
      ./moj_toplevel
 

lub

 
      ledit ./moj_toplevel
 

Oczywiście w przypadku jednego modułu, z tworzenia własnej wersji środowiska nie mamy wielkiego zysku.

ZałącznikWielkość
plik.mli90 bajtów
plik.ml135 bajtów
drugi_plik.ml119 bajtów
moduly.zip537 bajtów