OCaml/Pierwsze kroki

Jeśli mamy przygotowane środowisko programistyczne do nauki, możemy przerobić parę prostych programów. Uruchamiamy "ocaml" i wpisujemy pierwsze banalne przykłady:

$ ocaml
        Objective Caml version 3.09.3

# 5 + 5;;
- : int = 10

# 6.3 *. 2.5;;
- : float = 15.75

# 5 > 6;;
- : bool = false

# 'a';;
- : char = 'a'

# "abc";;
- : string = "abc"

# ();;
- : unit = ()

Fragmenty kodu są wpisane za znakiem zachęty '#' i w tym przypadku jest to pojedyncze wyrażenie "5 + 5", za którym znajduje się separator ";;". Caml używa podwójnego średnika do odseparowywania wyrażeń na najwyższym "poziomie" kodu (ang. top-level). Znak ten w wielu przypadkach się pomija, a wewnątrz funkcji do odseparowywania poszczególnych instrukcji używa się pojedynczego średnika.

Ocaml w odpowiedzi na nasz pierwszy przykład zwrócił taką informację: "- : int = 10", która zawiera wynikowy (ang. inferred) typ wyrażenia (int) oraz jego wartość (10). Łącznik oznacza, że wyrażeniu nie nadaliśmy żadnej nazwy. Gdyby jednak chcieć je jakoś nazwać?

# let a = 5 + 5;;
val a : int = 10

Ha! Teraz posiadamy "zmienną" o nazwie "a" i wartości 10. Ważny jest tutaj cudzysłów dookoła słowa "zmienna". Tak na prawdę powyższe wyrażenie nie jest taką zmienną jaką znamy z języków imperatywnych (czyli np. z C). Jest to tylko nazwane "globalne wyrażenie". Wiąże się to z pewnymi istotnymi cechami. Nie możemy dla przykładu zmienić wartości "a" bez stworzenia nowej etykiety (choćby o tej samej nazwie, ale co jak łatwo się przekonać jest czymś zupełnie innym). Caml zwraca uwagę na wielkość liter; wyrażenia/etykiety zaczynające się z dużej litery są zarezerwowane dla nazw modułów i np. konstruktorów typów wariacyjnych (o czym się przekonamy!).

W bardzo podobny sposób możemy w Camlu stworzyć funkcje:

# let add a b = a + b;;
val add : int -> int -> int = <fun>

# add 5 6;;
- : int = 11

# let a = 10;;
val a : int = 10

# add 5 (a * 5 + 5);;
- : int = 60

# let addfive = add 5;;
val addfive : int -> int = <fun>

# addfive 40;;
- : int = 45

# let add2 a b = 
	let sum = a + b in
	sum
;;
val add2 : int -> int -> int = <fun>

Tym razem wydedukowany typ wyrażenia z linii pierwszej jest trochę bardziej złożony: "int -> int -> int = <fun>". Oznacza on ni mniej ni więcej tylko funkcję, która przyjmuje dwa inty i zwraca int.

W linii czwartej widać sposób w jaki wywołujemy funkcje – nie używamy nawiasów wokół argumentów i nie oddzielamy ich przecinkami tak jak to ma miejsce w C. Nawiasów użyjemy w następnym przypadku, gdy będziemy chcieli podać jako argument funkcji wynik innego wyrażenia. Jeśliby ich tam zbrakło Ocaml wywołałby funkcję z parametrami 5 i a, po czym pomnożył wynik przez 5 i dodał 5.

Okrągłe nawiasy w Camlu spełniają podobną funkcję co klamry w C – odgradzają bloki kodu grupując wyrażenia. Zauważmy więc, że te operacje arytmetyczne w nawiasie, stanowią pewne wyrażenie, któremu podczas wartościowania nadany zostaje typ int o konkretnej wartości. W wielu przypadkach zamiast nawiasów można użyć również słów begin i end jeśli zwiększają one czytelność. Nieraz wyglądało by to jednak po prostu śmiesznie:

# add 5 begin a*5+5 end;;
- : int = 60

Wracając do naszego poprzedniego sporego przykładu; w linii 13 możemy zauważyć coś dziwnego. Wywołaliśmy naszą dwuargumentową funkcję tylko z jednym argumentem. I co? Błędu nie ma! Co więcej, wyrażenie zwróciło nam "coś" co zapisaliśmy pod etykietką addfive. Z wydedukowanego typu "int -> int = <fun>", widzimy, że add, wywołane z jednym argumentem zwróciło funkcję, która przyjmuje jeden argument typu int, i zwraca jednego inta!

Co więcej, jak się później okazało nasza nowa funkcja dodaje do podanego argumentu piątkę. Niesamowite! Co lepsze, ta charakterystyczna dla języków funkcyjnych cecha ma pewne praktyczne zastosowania. Przykładowo możemy stworzyć ogólną funkcję, z której następnie przez podanie paru argumentów stworzymy drugą pochodną funkcję. Możemy ją z kolei podać do innej funkcji, która przy jakiejś okazji ją wywoła. Uruchamianie funkcji bez podanych wszystkich argumentów nazywać będziemy częściową aplikacją/częściowym aplikowaniem (ang. partial application).

Ostatni przykład ilustruje sposób w jaki tworzy się lokalne wyrażenia, czyli np. etykiety lub funkcje wewnątrz innych funkcji. Używamy bardzo podobnego wyrażenia do let a = b;;, tylko zakończonego słowem "in" zamiast średników.