Datové typy Option, Result a Array v programovacím jazyku OCaml

12. 10. 2023
Doba čtení: 37 minut

Sdílet

Autor: Depositphotos
Na článek o datových typech Option, Result a Array v jazyku F# dnes navážeme. Ukážeme si, jak lze tytéž typy použít v OCamlu, ale popíšeme si také sémantiku funkce bind a dokonce i tvorbu „infixové“ funkce.

Obsah

1. Datové typy Option, Result a Array v programovacím jazyku OCaml

2. Datový typ Option

3. Pokus o přečtení hodnoty obalené typem Option a predikáty is_none a is_some

4. Ekvivalence dvou obalených hodnot

5. Datový typ Option a pattern matching

6. Praktický příklad – hledání prvku v seznamu

7. Zřetězení volání funkcí, které si předávají hodnoty typu Option – neidiomatické řešení

8. Zřetězení volání funkcí, které si předávají hodnoty typu Option – řešení založené na bind

9. Funkce Option.bind zapsaná formou infixového operátoru

10. Datový typ Result

11. Praktický příklad – realizace funkce pro výpočet podílu dvou celočíselných hodnot

12. Zřetězení funkcí s využitím Result.bind

13. Datový typ Array

14. Konstrukce pole: výčet prvků, opakování hodnoty v poli

15. Konstrukce pole s výpočtem hodnot jeho prvků

16. Indexování prvků polí v jazyce OCaml

17. Modifikace (mutace) prvků pole

18. Repositář s demonstračními příklady

19. Odkazy na Internetu

1. Datové typy Option, Result a Array v programovacím jazyku OCaml

Na tento týden vydaný článek o datových typech Option, Result a Array v programovacím jazyku F# dnes navážeme, protože si řekneme, jak se tyto (naprosto stejně pojmenované) datové typy používají v programovacím jazyku OCaml. Díky tomu, že se již dnes nebudeme muset věnovat podrobnějšímu popisu chování (sémantiky a vlastně i syntaxe) těchto datových typů, budeme mít mnohem více prostoru pro demonstrační příklady i pro praktické způsoby použití. Týká se to například funkce bind, kterou najde v praxi využití pro zřetězení většího množství operací. U všech dále uvedených demonstračních příkladů bude navíc vypsána i vypočtená či zobrazená hodnota tak, jak je ji možné získat z interaktivního prostředí utop popř. z interaktivní webové stránky https://try.ocaml.pro/ (všechny příklady byly v tomto prostředí ověřeny).

Poznámka: zpočátku, tj. v předchozích článcích, se nám mohlo zdát, že programovací jazyky OCaml a F# jsou prakticky totožné; pouze je nutné v OCamlu ukončovat příkazy dvojicí středníků. Některé rozdíly jsme již mohli vidět při práci se seznamy (rozdílné přístupy k hlavě a ocasu seznamu atd.) nebo u operátorů pro datový typ float. Ovšem ještě více rozdílů nalezneme při práci s poli, tedy s typem Array. Po sémantické stránce jsou pole v obou jazycích prakticky totožnými datovými typy, ovšem syntaxe práce s nimi je rozdílná. Týká se to například indexování prvků atd. – viz další text.

2. Datový typ Option

V programovacím jazyku OCaml se, ostatně naprosto stejně jako v jazyku F#, poměrně často používá datový typ nazvaný Option, a to ve chvílích, kdy je zapotřebí reprezentovat neznámou hodnotu, chybějící hodnotu (ovšem bez vyvolání výjimky), vytvořit funkci s volitelnými parametry či vytvořit typově bezpečnou obdobu odkazu.

Definice datového typu Option vypadá v jazyce OCaml takto:

type 'a t = 'a option =
    | None
    | Some of 'a

Naprosto stejně jako v jazyku F# i zde se jedná o výčtový typ s pouhými dvěma explicitně zapsanými hodnotami Some a None.

Hodnota None se vytvoří následujícím způsobem:

let noAnswer = None;;

Interaktivní prostředí OCamlu si odvodí typ této proměnné následovně:

val noAnswer : 'a option = None

Obalení konkrétní hodnoty do struktury Option lze realizovat stejně jako v jazyku F#:

let maybeAnswer = Some 42;;

Povšimněte si, že překladač si v tomto případě správně odvodil, že se jedná o datový typ Option a obalenou hodnotou je celé číslo 42 (což bude dále využito při typových inferencích):

val maybeAnswer : int option = Some 42

Můžeme si samozřejmě nechat obalit například i hodnotu typu řetězec:

let maybeHello = Some "Hello world";;

Výsledkem je proměnná tohoto typu:

val maybeHello : string option = Some "Hello world"

Ještě si pro úplnost ukažme nepatrně složitější příklad, v němž je použita n-tice:

let someTuple = Some (1, "foo", [1;2;3]);;

Typ proměnné odvozený překladačem vypadá takto:

val someTuple : (int * string * int list) option = Some (1, "foo", [1; 2; 3])

Jedná se sice o nepraktický příklad, ale nic nám nebrání v tom, abychom jednu hodnotu Option obalili do jiné hodnoty Option:

let ugh1 = Some (Some 42);;
let ugh2 = Some None;;

Příslušné otypované hodnoty, které jsme získali:

val ugh1 : int option option = Some (Some 42)
val ugh2 : 'a option option = Some None

3. Pokus o přečtení hodnoty obalené typem Option a predikáty is_none a is_some

Pro přečtení hodnoty, která je obalena typem Option, je možné v případě potřeby (ale nebude to příliš často) použít funkci nazvanou get z modulu Option. V případě, že se této funkci předá Some(hodnota), vrátí se příslušná hodnota, pokud se ovšem naopak předá hodnota None, dojde k výjimce. Toho chování si můžeme velmi snadno ověřit:

let answer1 = Some 42;;
let answer2 = None;;
 
Option.get answer1;;
Option.get answer2;;

Výsledkem bude v prvním případě hodnota 42 (se správným typem!), ve druhém případě vyhozená výjimka:

- : int = 42
 
Exception: (Invalid_argument "option is None")

V článku o jazyku F# jsme si řekli, že pro hodnoty typu Option(typ) jsou definovány i atributy (přesněji řečeno predikáty zapisované formou atributů), které nám umožní otestovat, zda pracujeme s hodnotou Some(typ) nebo None. Tyto atributy se jmenují IsNone a IsSome a vždy jeden z nich obsahuje hodnotu true a druhý (logicky) hodnotu false. Jejich použití je snadné (a typicky neidiomatické!).

let maybeAnswer = None
 
printf "IsNone: %b" maybeAnswer.IsNone
printf "IsSome: %b" maybeAnswer.IsSome
 
 
let maybeAnswer = Some 42
 
printf "IsNone: %b" maybeAnswer.IsNone
printf "IsSome: %b" maybeAnswer.IsSome

V programovacím jazyku OCaml není nutné vymýšlet novou sémantiku, protože programátor má k dispozici skutečné predikáty, tedy funkce, které vrací na základě svého parametru hodnotu true či false. Tyto predikáty se – nepřekvapivě – jmenují is_none a is_some; podobně jako get je nalezneme v balíčku Option:

let answer1 = Some 42;;
let answer2 = None;;
 
Option.is_none answer1;;
Option.is_some answer1;;
Option.is_none answer2;;
Option.is_some answer2;;

Výsledkem bude postupně čtveřice hodnot

- : bool = false
- : bool = true
- : bool = true
- : bool = false

4. Ekvivalence dvou obalených hodnot

V některých algoritmech se setkáme s potřebou zjistit, zda jsou dvě (potenciálně) obalené hodnoty ekvivalentní. K tomuto účelu se v praxi používá funkce (a to dokonce funkce vyššího řádu) nazvaná equal, kterou opět nalezneme v modulu Option. Jak tato funkce vlastně pracuje? Mohou nastat tři případy:

  1. Pokud jsou obě vstupní hodnoty None, je výsledkem true
  2. Pokud jsou obě vstupní hodnoty Some(x) a Some(y), vrátí se výsledek porovnání eq x y, kde funkci eq je nutné dodat (viz demonstrační příklad)
  3. Ve všech ostatních případech (porovnání None a Some(x)) nebo pokud x!=y, se vrátí hodnota false

Podívejme se na následující demonstrační příklad, z něhož by mělo být chování funkce Option.equal zřejmé:

let answer1 = Some 42;;
let answer2 = Some 42;;
let answer3 = Some 0;;
let answer4 = None;;
let answer5 = None;;
 
let int_equal x y =
  x = y;;
 
Option.equal int_equal answer1 answer2;;
Option.equal int_equal answer1 answer3;;
Option.equal int_equal answer1 answer4;;
Option.equal int_equal answer1 answer5;;
Option.equal int_equal answer2 answer3;;
Option.equal int_equal answer2 answer4;;
Option.equal int_equal answer2 answer5;;
Option.equal int_equal answer3 answer4;;
Option.equal int_equal answer3 answer5;;
Option.equal int_equal answer4 answer5;;

Výsledky ukazují, že výše uvedené předpoklady platí:

Option.equal int_equal answer1 answer2 ;;
- : bool = true
 
Option.equal int_equal answer1 answer3 ;;
- : bool = false
 
Option.equal int_equal answer1 answer4 ;;
- : bool = false
 
Option.equal int_equal answer1 answer5 ;;
- : bool = false
 
Option.equal int_equal answer2 answer3 ;;
- : bool = false
 
Option.equal int_equal answer2 answer4 ;;
- : bool = false
 
Option.equal int_equal answer2 answer5 ;;
- : bool = false
 
Option.equal int_equal answer3 answer4 ;;
- : bool = false
 
Option.equal int_equal answer3 answer5 ;;
- : bool = false
 
Option.equal int_equal answer4 answer5 ;;
- : bool = true
Poznámka: ve skutečnosti funkce int_equal neporovnává pouze dvě celočíselné hodnoty, protože její typ je:
val int_equal : 'a -> 'a -> bool = <fun>

což znamená, že se porovnávají dvě hodnoty libovolného typu. Úprava na porovnání pouze celých čísel může vypadat například takto:

let int_equal (x:int) y =
  x = y;;

Typ této nové funkce odpovídá:

val int_equal : int -> int -> bool = <fun>
Poznámka: uvedením typu prvního parametru jsme vlastně překladači vnutili i typ parametru druhého, což je odvozeno z definice operátoru porovnání.

Samozřejmě by bylo možné typ funkce nepřímo překladači vnutit i například takto:

let int_equal x y =
  x = y+0;;

se stejným odvozením typů:

val int_equal : int -> int -> bool = <fun>

5. Datový typ Option a pattern matching

Způsob kombinace datového typu Option s technologií pattern matchingu jsme si již ukázali v článku o programovacím jazyku F#. V OCamlu vše pracuje prakticky totožným způsobem, což je dobře, protože pattern matching nám totiž umožňuje velmi elegantním způsobem hodnoty typu Option(typ) zpracovávat.

Demonstrační příklad, který vrací hodnotu true či false na základě toho, zda funkci předáme obalenou hodnotu či None, můžeme do OCamlu přepsat takto:

let exists x =
  match x with
  | Some(x) -> true
  | None -> false;;
 
let maybeAnswer1 = Some 42;;
let maybeAnswer2 = None;;
 
exists maybeAnswer1;;
exists maybeAnswer2;;

Výsledkem bude – naprosto podle očekávání – tato dvojice zpráv:

true
false

Díky tomu, že překladač ví, jak přesně je definován typ Option, dokáže detekovat takový programový kód, v němž nejsou pokryty všechny možnosti, které mohou v runtime nastat. Podívejme se například na funkci, v níž jsme „omylem“ vynechali vzor None s jeho větví:

let exists x =
  match x with
  | Some(x) -> true
;;
 
let maybeAnswer1 = Some 42;;
let maybeAnswer2 = None;;
 
exists maybeAnswer1;;
exists maybeAnswer2;;

Překladač v tomto případě přesně detekuje problém, což je patrné z následujícího screenshotu:

Obrázek 1: Detekce chybějící větve None ve webovém prostředí.

Pokusme se naopak vynechat vzor Some s jeho větví:

let exists x =
  match x with
  | None -> false;;
 
let maybeAnswer1 = Some 42;;
let maybeAnswer2 = None;;
 
exists maybeAnswer1;;
exists maybeAnswer2;;

Překladač opět přesně určí, kde nastala chyba:

Obrázek 2: Detekce chybějící větve Some ve webovém prostředí.

6. Praktický příklad – hledání prvku v seznamu

Stejně jako v případě programovacího jazyka F# můžeme i v OCamlu poměrně elegantním způsobem zapsat algoritmus vyhledání prvku v seznamu. V případě, že prvek bude nalezen, vrátí se jeho hodnota obalená do Some(typ), pokud nalezen naopak nebude, vrátí se hodnota None (nemusíme tedy řešit nějaké „magické hodnoty“ pro signalizaci, že prvek nebyl nalezen). Typ prvků bude odvozen překladačem:

let rec find list value =
  match list with
  | head :: tail -> if head = value then Some(head) else find tail value
  | [] -> None
;;
 
let list1 = [1; 2; 3; 4];;
let list2 = [0; 0; 0; 0];;
let list3 = [];;
let list4 = [3];;
 
find list1 3;;
find list2 3;;
find list3 3;;
find list4 3;;

Výsledky získané tímto příkladem vypadají takto:

find list1 3 ;;
- : int option = Some 3
 
find list2 3 ;;
- : int option = None
 
find list3 3 ;;
- : int option = None
 
find list4 3 ;;
- : int option = Some 3

Tisk hodnoty (ať již existující nebo neexistující) můžeme pochopitelně realizovat vlastní funkcí, zde konkrétně funkcí nazvanou (poněkud obecně) print_item. I v této funkci použijeme pattern matching:

let rec find list value =
  match list with
  | head :: tail -> if head = value then Some(head) else find tail value
  | [] -> None
;;
 
let print_item value =
  match value with
  | Some(x) -> Printf.printf "%d\n" x
  | None -> Printf.printf "not found\n"
;;
 
let list1 = [1; 2; 3; 4];;
let list2 = [0; 0; 0; 0];;
let list3 = [];;
let list4 = [3];;
 
print_iten (ind list1 3);;
print_iten (ind list2 3);;
print_iten (ind list3 3);;
print_iten (ind list4 3);;

7. Zřetězení volání funkcí, které si předávají hodnoty typu Option – neidiomatické řešení

Poměrně často se v praxi setkáme s nutností zřetězit několik funkcí do takzvané pipeline (možná můžeme stále používat postarší termín kolona, který celý proces dobře vystihuje). To znamená, že jedna funkce pošle své výsledky do další funkce, ta pošle výsledky do další funkce atd. atd. To může být poměrně elegantní řešení, které má navíc v některých jazycích podporu v syntaxi (threading makro v Clojure atd.). Ovšem může se stát, že funkce nebudou přímo produkovat výsledné hodnoty, ale obalí je buď do struktury Option nebo Result (a to podle povahy řešeného problému).

Podívejme se na uměle vytvořený příklad, v němž se vstupní hodnota získá vyhledáním prvku v seznamu. V dalším kroku se hodnota nalezeného prvku zdvojnásobí a přičte se k ní jednička. Problém spočívá v tom, že už první funkce může vrátit None, pokud prvek nenalezne. A naše řešení s tím musí počítat. Naivní implementace tedy bude ve všech funkcích vyžadovat, aby akceptovaly typ Option, což není ani ideální ani dobře čitelné (a už vůbec ne idiomatické):

let rec find list value =
  match list with
  | head :: tail -> if head = value then Some(head) else find tail value
  | [] -> None
;;
 
let print_item value =
  match value with
  | Some(x) -> Printf.printf "%d\n" x
  | None -> Printf.printf "not found\n"
;;
 
let double value : int option =
  match value with
  | Some(x) -> Some(2*x)
  | None -> None
;;
 
let inc value : int option =
  match value with
  | Some(x) -> Some(x+1)
  | None -> None
;;
 
 
let list1 = [1; 2; 3; 4];;
let list2 = [0; 0; 0; 0];;
let list3 = [];;
let list4 = [3];;
 
 
print_item (inc (double (find list1 3)));;
print_item (inc (double (find list2 3)));;
print_item (inc (double (find list3 3)));;
print_item (inc (double (find list4 3)));;

Výsledky sice budou korektní, ale krásou tento program neoplývá:

print_item (inc (double (find list1 3))) ;;
7
 
print_item (inc (double (find list2 3))) ;;
not found
 
print_item (inc (double (find list3 3))) ;;
not found
 
print_item (inc (double (find list4 3))) ;;
7

8. Zřetězení volání funkcí, které si předávají hodnoty typu Option – řešení založené na bind

Předchozí příklad ukázal problém, s nímž se setkáváme prakticky vždy při vytváření pipeline/kolony. Řešení spočívá v tom, že namísto kolony vlastně vytvoříme obdobu tohoto kolejiště – https://miro.medium.com/v2/re­size:fit:1280/1*pxXvepfiD­ZlsO2X-KSwFqQ.png. Zelená část kolejiště odpovídá tzv. „happy path“, tj. v našem případě situaci, kdy funkce budou vracet hodnoty Some(typ) a na červenou část se odbočí ve chvíli, kdy nějaká funkce vrátí None (ještě názornější to bude u typu Result). Po odbočení do červené části se již nemůžeme vrátit zpět (vlaky jedou zleva doprava).

A pro zjednodušení programování „výhybek“ i vlastních funkcí slouží velmi užitečná funkce nazvaná Option.bind. Její typ je následující:

val bind : 'a option -> ('a -> 'b option) -> 'b option

což znamená, že tato funkce má dva parametry – dokážete je již přečíst?

Důležité je chování této funkce:

  1. Pokud je do bind předána hodnota None, vrátí se hodnota None
  2. Pokud je předána hodnota Some(x), je zavolána funkce f s extrahovanou hodnotou x
  3. Výsledkem musí být hodnota Option

Co to v praxi znamená? Předchozí demonstrační příklad můžeme upravit například do této (prozatím nepříliš elegantní) podoby:

let rec find list value =
  match list with
  | head :: tail -> if head = value then Some(head) else find tail value
  | [] -> None
;;
 
let double value =
  Some(2*value)
;;
 
let inc value =
  Some(value+1)
;;
 
 
let list1 = [1; 2; 3; 4];;
let list2 = [0; 0; 0; 0];;
let list3 = [];;
let list4 = [3];;
 
Option.bind (Option.bind (find list1 3) double) inc;;
Option.bind (Option.bind (find list2 3) double) inc;;
Option.bind (Option.bind (find list3 3) double) inc;;
Option.bind (Option.bind (find list4 3) double) inc;;

Výsledky by měly odpovídat očekávání:

Option.bind (Option.bind (find list1 3) double) inc ;;
- : int option = Some 7
 
Option.bind (Option.bind (find list2 3) double) inc ;;
- : int option = None
 
Option.bind (Option.bind (find list3 3) double) inc ;;
- : int option = None
 
Option.bind (Option.bind (find list4 3) double) inc; ;;
- : int option = Some 7

9. Funkce Option.bind zapsaná formou infixového operátoru

Příklad z předchozí kapitoly byl sice čitelnější, než příklad ze sedmé kapitoly, ale můžeme se pokusit o jeho další vylepšení. To spočívá v tom, že si funkci Option.bind namapujeme na infixový operátor >>= (ano, i to je v OCamlu možné, i když je nutné dodržovat pravidla pro infixové operátory, k čemuž se vrátíme příště). Samotný zápis je až triviální, nesmíme ovšem zapomenout na závorky okolo jména funkce:

let (>>=) = Option.bind ;;
Poznámka: ve skutečnosti jsme definovali symbol pro funkci, která ovšem splňuje pravidla pro pojmenování infixových operátorů, takže se zapisuje v infixové podobě (mezi své dva parametry – operandy).

To nám umožňuje zapsat zřetězení tak, že bude zřejmé již při letmém pohledu na kód:

(find list1 3) >>= double >>= inc;;
(find list2 3) >>= double >>= inc;;
(find list3 3) >>= double >>= inc;;
(find list4 3) >>= double >>= inc;;

Upravený kód demonstračního příkladu bude vypadat takto:

let rec find list value =
  match list with
  | head :: tail -> if head = value then Some(head) else find tail value
  | [] -> None
;;
 
let double value =
  Some(2*value)
;;
 
let inc value =
  Some(value+1)
;;
 
 
let list1 = [1; 2; 3; 4];;
let list2 = [0; 0; 0; 0];;
let list3 = [];;
let list4 = [3];;
 
let (>>=) = Option.bind ;;
 
(find list1 3) >>= double >>= inc;;
(find list2 3) >>= double >>= inc;;
(find list3 3) >>= double >>= inc;;
(find list4 3) >>= double >>= inc;;

Výsledky přitom budou stejné, jako v předchozím příkladu.

10. Datový typ Result

I s datovým typem Results jsme se již seznámili. V programovacích jazycích F# a OCaml (a nutno dodat, že nejenom v nich) se používá ve chvílích, kdy je nutné z nějaké funkce či výrazu vrátit buď vypočtenou hodnotu nebo informaci o chybě. Zde nám tedy již typ Option s hodnotami Some a None přestává vyhovovat. Pro příklad nemusíme chodit daleko (a taktéž ho známe) – předpokládejme, že budeme chtít, aby námi definovaná funkce pro dělení celých čísel vracela v případě pokusu o dělení nulou chybové hlášení a nikoli nicneříkající hodnotu None.

A přesně k tomuto účelu se jak v F#, tak i v OCamlu používá datový typ nazvaný příhodně Result. Tento datový typ se podobá již popsanému typu Option, ovšem s tím rozdílem, že obaluje buď výsledek (třeba návratovou hodnotu volané funkce) nebo informaci o chybě. Deklarace struktury Result vypadá následovně:

type ('a, 'e) t = ('a, 'e) result =
| Ok of 'a
| Error of 'e
Poznámka: povšimněte si, že se generické typy 'a a 'e od sebe mohou lišit, což je, když se nad problémem zamyslíme, logický požadavek – informace o chybě je jiného typu než vracená hodnota.

11. Praktický příklad – realizace funkce pro výpočet podílu dvou celočíselných hodnot

Funkci pro výpočet podílu dvou celých čísel můžeme zapsat takovým způsobem, aby se v těle funkce detekoval pokus o dělení nulou. V takovém případě vrátíme ve struktuře Result informaci o chybě, ve všech ostatních případech pak vrátíme výsledek, ovšem opět obalený do typu Result:

let divide (x:int) (y:int) =
  match y with
  | 0 -> Error "divide by zero"
  | _ -> Ok (x/y)
;;
 
divide 10 2;;
divide 10 0;;

Výsledky, které získáme pro dvě dvojice vstupních parametrů:

divide 10 2 ;;
- : (int, string) result = Ok 5
 
divide 10 0 ;;
- : (int, string) result = Error "divide by zero"

Ve skutečnosti je typová inference jazyka OCaml tak dobrá, že můžeme vynechat i explicitní určení typů parametrů naší funkce:

let divide x y =
  match y with
  | 0 -> Error "divide by zero"
  | _ -> Ok (x/y)
;;
 
divide 10 2;;
divide 10 0;;

Odvozený typ funkce divide je i v tomto případě následující:

val divide : int -> int -> (int, string) result = <fun>

Samozřejmě si můžeme nadeklarovat pomocnou funkci, která zobrazí buď vypočtenou hodnotu, nebo informaci s chybou, která při výpočtu nastala:

let divide (x:int) (y:int) =
    match y with
    | 0 -> Error "divide by zero"
    | _ -> Ok (x/y)
;;
 
let print_result value =
    match value with
    | Ok(value) -> Printf.printf "%d\n" value
    | Error(error) -> Printf.printf "%s\n" error
;;
 
print_result (divide 10 2);;
print_result (divide 1 0);;

Výsledky budou v tomto případě vypadat následovně:

divide 10 2;;
- : (int, string) result = Ok 5
 
divide 10 0 ;;
- : (int, string) result = Error "divide by zero"

Jen pro úplnost – odstranění nadbytečných informací o typech parametrů funkce divide:

let divide x y =
    match y with
    | 0 -> Error "divide by zero"
    | _ -> Ok (x/y)
;;
 
let print_result value =
    match value with
    | Ok(value) -> Printf.printf "%d\n" value
    | Error(error) -> Printf.printf "%s\n" error
;;
 
print_result (divide 10 2);;
print_result (divide 1 0);;

12. Zřetězení funkcí s využitím Result.bind

V sedmé až deváté kapitole jsme si ukázali způsob implementace jedné z forem railway oriented programmingu. Ve skutečnosti se s touto formou programování setkáme spíše v souvislosti se strukturou Result a nikoli Option, i když obě možnosti jsou zcela legální. Zkusme si nyní vytvořit následující kolonu:

  1. Vypočteme podíl dvou hodnot (což může vést k chybě)
  2. Výsledek vynásobíme dvěma
  3. Předchozí výsledek zvýšíme o jedničku
  4. Otestujeme, zda hodnota není větší než 5 (pokud ano, dojde k chybě)

Jedno z možných řešení může vypadat takto. Až na jméno infixové funkce (lidé volí různé tvary) se jedná o idiomatické řešení:

let divide (x:int) (y:int) =
  match y with
  | 0 -> Error "divide by zero"
  | _ -> Ok (x/y)
;;
 
let double value =
  Ok(2*value)
;;
 
let inc value =
  Ok(value+1)
;;
 
let tooHigh value =
  if value > 5 then Error "too high" else Ok(value)
;;
 
let (>>=) = Result.bind ;;
 
(divide 2 1) >>= double >>= inc >>= tooHigh;;
(divide 2 0) >>= double >>= inc >>= tooHigh;;
(divide 20 1) >>= double >>= inc >>= tooHigh;;

Zobrazené výsledky ukazují, jak elegantně se detekovaly a zpracovaly oba typy chyb:

(divide 2 1) >>= double >>= inc >>= tooHigh ;;
- : (int, string) result = Ok 5
 
(divide 2 0) >>= double >>= inc >>= tooHigh ;;
- : (int, string) result = Error "divide by zero"
 
(divide 20 1) >>= double >>= inc >>= tooHigh ;;
- : (int, string) result = Error "too high"
Poznámka: pro zajímavost si můžete vyzkoušet jednotlivé body výpočtu různě prohazovat – výhybky na „červenou kolej“ budou stále funkční.

13. Datový typ Array

Datový typ Array má v jazyce OCaml podobné vlastnosti, jako je tomu v jazyce F#. Podobně jako u seznamů, i u pole platí, že se jedná o homogenní datový typ, což znamená, že všechny jeho prvky jsou stejného typu. Přístup k prvkům se uskutečňuje přes indexy tak, jak jsme na to zvyklí z dalších programovacích jazyků, ovšem s tím rozdílem, že syntaxe je značně neobvyklá.

Připomeňme si, jaké jsou základní rozdíly mezi poli a seznamy:

Operace Seznam Pole
přidání prvku O(1) O(n)
přístup k prvku O(n) O(1)
modifikace prvku × O(1)
Poznámka: přidání prvku vrací nový seznam nebo nové pole (u seznamů se pro přidání prvku na začátek používá operátor ::).

Seznamy se tedy používají zejména tehdy, pokud algoritmy umožní sekvenční procházení seznamem (s tím, že se vlastně neustále odděluje hlava od ocasu seznamu) a není zapotřebí prvky modifikovat. Naopak pole umožňuje náhodný přístup k prvkům a prvky lze modifikovat (mutovat). V budoucnosti se ovšem pravděpodobně dočkáme i přidání podpory pro neměnitelná pole, která tak budou moci využít výhod obou datových typů a navíc umožní například bezpečné vytváření řezů atd.

14. Konstrukce pole: výčet prvků, opakování hodnoty v poli

Pole můžeme v jazyku OCaml zkonstruovat několika možnými způsoby. Základem je přitom konstrukce založená na zápisu výčtu hodnot všech prvků, které mají být v poli uloženy. Tento zápis (syntaxe) vypadá následovně:

let a = [| 1; 2; 3; 4; |];;

Výsledkem bude proměnná, jejíž hodnota a typ vypadá takto:

val a : int array = [|1; 2; 3; 4|]

Obdobou funkce Array.create z jazyka F# je v OCamlu funkce nazvaná Array.make, která má sice odlišné jméno, ale chová se podobně:

let a = Array.make 10 42;;
 
a;;

Po konstrukci pole tímto způsobem získáme datovou strukturu s deseti prvky s hodnotou 42:

- : int array = [|42; 42; 42; 42; 42; 42; 42; 42; 42; 42|

15. Konstrukce pole s výpočtem hodnot jeho prvků

Pole lze ovšem v případě potřeby zkonstruovat i mnoha dalšími způsoby. Poměrně elegantním řešením může být použití funkce/konstruktoru Array.init (pojmenovaná stejně v OCamlu i v jazyku F#), které se předává počet prvků pole a taktéž funkce, která je použita pro výpočet hodnoty n-tého prvku na základě indexu n. Můžeme si tak snadno vytvořit například aritmetickou posloupnost od 1 do n (tj.k indexu prvku přičteme jedničku – indexy totiž začínají od nuly a my budeme chtít, aby hodnota prvního prvku byla jednička):

let next_x x =
  x+1;;
 
let a = Array.init 10 next_x;;
 
a;;

Výsledkem konstrukce pole tímto způsobem bude následující proměnná

val a : int array = [|1; 2; 3; 4; 5; 6; 7; 8; 9; 10|]

Samozřejmě je možné výpočet různým způsobem upravovat, například do podoby:

let next_x x =
  x*2;;
 
let a = Array.init 10 next_x;;
 
a;;

S výsledkem:

val a : int array = [|0; 2; 4; 6; 8; 10; 12; 14; 16; 18|]

Samozřejmě si můžeme nechat vytvořit pole s prvky odlišného typu:

let next_x x =
  1./.(float_of_int x +. 1.);;
 
let a = Array.init 10 next_x;;
 
a;;

S výsledkem:

val a : float array =
  [|1.; 0.5; 0.333333333333333315; 0.25; 0.2; 0.166666666666666657;
    0.142857142857142849; 0.125; 0.111111111111111105; 0.1|]

16. Indexování prvků polí v jazyce OCaml

Způsob přístupu k prvkům polí je v jazyce OCaml poněkud neobvyklý a budete si na něj několik dnů zvykat. Problém spočívá v tom, že namísto dnes používaného zápisu pole[index] se totiž používá zápis pole.(index), což je sice jednoznačné (nebude se to plést s definicí seznamu atd.), ovšem skutečně zvláštní. Ostatně podívejme se na jednoduchý příklad, v němž se pokusíme přistoupit k prvnímu prvku pole, k prvku druhému a –1 (což by například v Pythonu bylo korektní). Tento příklad nám navíc ukazuje, že se prvky pole v jazyce OCaml indexují od nuly a nikoli od jedničky:

let a = [| 1; 2; 3; 4; |];;
 
a.(0);;
a.(1);;
a.(-1);;

Z výsledků je patrné především to, že přístup k prvku s indexem –1 vyvolal výjimku:

a.(0) ;;
- : int = 1
 
a.(1) ;;
- : int = 2
 
a.(-1) ;;
Exception: (Invalid_argument "index out of bounds")

17. Modifikace (mutace) prvků pole

Z článku o jazyku F# již víme, že důležitou vlastností polí (kterou se navíc pole odlišují od seznamů) je fakt, že prvky pole je možné modifikovat neboli mutovat. K tomuto účelu slouží operátor ← , který již opět poměrně velmi dobře známe:

let a = [| 1; 2; 3; 4; |];;
a;;
 
a.(0) <- 42;;
a;;
 
a.(1) <- 6502;;
a;;

Podívejme se nyní, jaké hodnoty (pole) se postupně vypíšou po spuštění tohoto příkladu v interaktivním prostředí:

linux_sprava_tip

- : int array = [|1; 2; 3; 4|]
 
- : int array = [|42; 2; 3; 4|]
 
- : int array = [|42; 6502; 3; 4|]

18. Repositář s demonstračními příklady

Všechny výše popsané demonstrační příklady byly uloženy do repositáře dostupného na adrese https://github.com/tisnik/ocaml-examples/. V tabulce umístěné pod tímto odstavcem jsou uvedeny odkazy na tyto příklady:

# Příklad Popis příkladu Cesta
1 article01/hello_world1.ml zavolání funkce print_string https://github.com/tisnik/ocaml-examples/tree/master/arti­cle01/hello_world1.ml
2 article01/hello_world2.ml zavolání funkce printf.Printf https://github.com/tisnik/ocaml-examples/tree/master/arti­cle01/hello_world2.ml
       
3 article01/function.ml definice funkce https://github.com/tisnik/ocaml-examples/tree/master/arti­cle01/function.ml
4 article01/lambda.ml anonymní funkce https://github.com/tisnik/ocaml-examples/tree/master/arti­cle01/lambda.ml
       
5 article01/function_type1.ml explicitní specifikace typu návratové hodnoty funkce https://github.com/tisnik/ocaml-examples/tree/master/arti­cle01/function_type1.ml
6 article01/function_type2.ml explicitní specifikace typu návratové hodnoty funkce https://github.com/tisnik/ocaml-examples/tree/master/arti­cle01/function_type2.ml
       
7 article01/call_function1.ml definice jednoduché funkce s jejím zavoláním https://github.com/tisnik/ocaml-examples/tree/master/arti­cle01/call_function1.ml
8 article01/call_function2.ml definice jednoduché funkce s jejím zavoláním https://github.com/tisnik/ocaml-examples/tree/master/arti­cle01/call_function2.ml
9 article01/call_function3.ml použití operátoru + pro dvojici hodnot typu float https://github.com/tisnik/ocaml-examples/tree/master/arti­cle01/call_function3.ml
10 article01/call_function4.ml použití operátoru +. pro dvojici hodnot typu float https://github.com/tisnik/ocaml-examples/tree/master/arti­cle01/call_function4.ml
11 article01/call_function5.ml plná deklarace funkce bez syntaktického cukru https://github.com/tisnik/ocaml-examples/tree/master/arti­cle01/call_function5.ml
12 article01/call_function6.ml plná deklarace funkce bez syntaktického cukru https://github.com/tisnik/ocaml-examples/tree/master/arti­cle01/call_function6.ml
       
13 article01/local_binding1.ml definice lokálních symbolů https://github.com/tisnik/ocaml-examples/tree/master/arti­cle01/local_binding1.ml
14 article01/local_binding2.ml definice lokálních symbolů https://github.com/tisnik/ocaml-examples/tree/master/arti­cle01/local_binding2.ml
       
15 article02/basic_binding.ml navázání hodnoty na symbol (deklarace proměnné) https://github.com/tisnik/ocaml-examples/tree/master/arti­cle02/basic_binding.ml
16 article02/print_variable.ml tisk hodnoty proměnné https://github.com/tisnik/ocaml-examples/tree/master/arti­cle02/print_variable.ml
17 article02/variables_and_functions.ml předání proměnné do funkce https://github.com/tisnik/ocaml-examples/tree/master/arti­cle02/variables_and_functi­ons.ml
18 article02/redefine_symbol1.ml pokus o redefinici symbolu https://github.com/tisnik/ocaml-examples/tree/master/arti­cle02/redefine_symbol1.ml
19 article02/redefine_symbol2.ml pokus o redefinici symbolu (složitější příklad) https://github.com/tisnik/ocaml-examples/tree/master/arti­cle02/redefine_symbol2.ml
       
20 article02/requal_operator1.ml operátor = https://github.com/tisnik/ocaml-examples/tree/master/arti­cle02/equal_operator1.ml
21 article02/requal_operator2.ml operátor = https://github.com/tisnik/ocaml-examples/tree/master/arti­cle02/equal_operator2.ml
       
22 article02/immutable_variable.ml „změna“ neměnitelné proměnné https://github.com/tisnik/ocaml-examples/tree/master/arti­cle02/immutable_variable.ml
22 article02/mutable_variable.ml změna měnitelné proměnné https://github.com/tisnik/ocaml-examples/tree/master/arti­cle02/mutable_variable.ml
23 article02/shadow.ml shadowing symbolu https://github.com/tisnik/ocaml-examples/tree/master/arti­cle02/shadow.ml
24 article02/incr.ml standardní funkce incr https://github.com/tisnik/ocaml-examples/tree/master/arti­cle02/incr.ml
25 article02/ident.ml nejjednodušší polymorfická funkce https://github.com/tisnik/ocaml-examples/tree/master/arti­cle02/ident.ml
       
26 article02/tuple1.ml datový typ n-tice (tuple) https://github.com/tisnik/ocaml-examples/tree/master/arti­cle02/tuple1.ml
27 article02/tuple2.ml datový typ n-tice (tuple) https://github.com/tisnik/ocaml-examples/tree/master/arti­cle02/tuple2.ml
28 article02/record1.ml datový typ záznam (record), deklarace proměnné tohoto typu https://github.com/tisnik/ocaml-examples/tree/master/arti­cle02/record1.ml
29 article02/record2.ml datový typ záznam (record) a typová inference https://github.com/tisnik/ocaml-examples/tree/master/arti­cle02/record2.ml
       
30 article02/unit.ml datový typ unit a rozdíl oproti funkcím bez parametrů https://github.com/tisnik/ocaml-examples/tree/master/arti­cle02/unit.ml
31 article02/polymorphic.ml použití polymorfických funkcí https://github.com/tisnik/ocaml-examples/tree/master/arti­cle02/polymorphic.ml
32 article02/two_same_records.ml dva datové typy záznam se shodnými prvky https://github.com/tisnik/ocaml-examples/tree/master/arti­cle02/two_same_records.ml
       
33 article03/recursion1.ml pokus o deklaraci funkce s přímou rekurzí založený na let https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/recursion1.ml
34 article03/recursion2.ml deklarace funkce s přímou rekurzí založená na let rec https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/recursion2.ml
35 article03/recursion3.ml využití tail rekurze pro výpočet členu Fibonacciho posloupnosti https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/recursion3.ml
36 article03/recursion4.ml obyčejná nerekurzivní funkce definovaná přes let rec https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/recursion4.ml
       
37 article03/odd_even1.ml nepřímá rekurze (nekorektní varianta) https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/odd_even1.ml
38 article03/odd_even2.ml nepřímá rekurze (taktéž nekorektní varianta) https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/odd_even2.ml
39 article03/odd_even3.ml jediný korektní zápis nepřímé rekurze https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/odd_even3.ml
40 article03/odd_even4.ml nepřímá rekurze bez použití klíčového slova rec https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/odd_even4.ml
       
41 article03/pattern1.ml výpočet Faktoriálu založený na pattern matchingu https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/pattern1.ml
42 article03/pattern2.ml výpočet Faktoriálu založený na pattern matchingu, sloučení vstupů se stejným výstupem https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/pattern2.ml
43 article03/pattern3.ml kontrola neplatného vstupu https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/pattern3.ml
44 article03/pattern4.ml pattern matching pro větší množství hodnot https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/pattern4.ml
45 article03/pattern5.ml rekurzivní implementace Ackermannovy funkce https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/pattern5.ml
46 article03/pattern6.ml kontrola neplatných vstupních hodnot pro Ackermannovu funkci https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/pattern6.ml
       
47 article03/fibonacci1.ml výpočet Fibonacciho posloupnosti založený na pattern matchingu https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/fibonacci1.ml
48 article03/fibonacci2.ml výpočet Fibonacciho posloupnosti založený na pattern matchingu (více idiomatický zápis) https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/fibonacci2.ml
       
49 article03/first.ml funkce vracející první prvek z dvojice založená na pattern matchingu https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/first.ml
50 article03/second.ml funkce vracející druhý prvek z dvojice založená na pattern matchingu https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/second.ml
51 article03/zero_coordinate.ml test na nulovou souřadnici/souřadnice založený na pattern matchingu https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/zero_coordinate.ml
       
52 article03/get_model.ml získání prvku ze záznamu (opět založeno na pattern matchingu) https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/get_model.ml
       
53 article03/list_literal1.ml seznam se třemi prvky typu celé číslo https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/list_literal1.ml
54 article03/list_literal2.ml seznam se třemi prvky typu řetězec https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/list_literal2.ml
55 article03/list_literal3.ml seznam se třemi prvky typu n-tice https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/list_literal3.ml
56 article03/list_literal4.ml nekorektní pokus o vytvoření seznamu s prvky různých typů https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/list_literal4.ml
57 article03/empty_list.ml konstrukce prázdného seznamu https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/empty_list.ml
       
58 article03/head_tail1.ml složení seznamu se dvěma prvky s využitím operátoru :: https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/head_tail1.ml
59 article03/head_tail2.ml složení seznamu se třemi prvky s využitím operátoru :: https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/head_tail2.ml
       
60 article03/list_properties.ml vlastnosti (properties) seznamů https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/list_properties.ml
61 article03/len1.ml naivní rekurzivní výpočet délky seznamu https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/len1.ml
62 article03/len2.ml vylepšený rekurzivní výpočet délky seznamu https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/len2.ml
63 article03/len3.ml vylepšený rekurzivní výpočet délky seznamu https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/len3.ml
       
64 article03/join_lists.ml spojení dvou seznamů operátorem :: https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/join_lists.ml
65 article03/append1.ml implementace spojení dvou seznamů rekurzivním výpočtem https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/append1.ml
66 article03/append2.ml implementace spojení dvou seznamů rekurzivním výpočtem, použití pattern matchingu https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/append2.ml
       
67 article03/sum1.ml součet hodnot všech prvků v seznamu (bez tail rekurze) https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/sum1.ml
68 article03/sum2.ml součet hodnot všech prvků v seznamu (s využitím tail rekurze) https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/sum2.ml
       
69 article03/print_int_list.ml tisk seznamu celých čísel https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/print_int_list.ml
70 article03/print_string_list.ml tisk seznamu řetězců https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/print_string_list.ml
71 article03/print_list_prefix.ml tisk seznamu s prefixem https://github.com/tisnik/ocaml-examples/tree/master/arti­cle03/print_list_prefix.ml
       
72 article04/none_value.ml hodnota None https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/none_value.ml
73 article04/some_value1.ml hodnota Some(typ) https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/some_value1.ml
74 article04/some_value2.ml hodnota Some(typ) https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/some_value2.ml
75 article04/some_value3.ml hodnota Some(typ) https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/some_value3.ml
76 article04/option_exists1.ml základní pattern matching, korektní varianta https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/option_exists1.ml
77 article04/option_exists2.ml základní pattern matching, nekorektní varianta https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/option_exists2.ml
78 article04/option_exists3.ml základní pattern matching, nekorektní varianta https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/option_exists3.ml
79 article04/find_in_list1.ml vyhledávání prvku v seznamu založené na pattern matchingu https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/find_in_list1.ml
80 article04/find_in_list2.ml varianta předchozího programu https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/find_in_list2.ml
81 article04/option_get.ml pokus o přečtení hodnoty obalené typem Option https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/option_get.ml
82 article04/is_none_is_some.ml predikáty is_none a is_some https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/is_none_is_some.ml
83 article04/option_equal.ml ekvivalence dvou obalených hodnot https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/option_equal.ml
84 article04/some_none.ml obalení obalené hodnoty https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/some_none.ml
       
85 article04/result_divide1.ml ukázka použití datového typu Result https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/result_divide1.ml
86 article04/result_divide2.ml ukázka použití datového typu Result a pattern matchingu https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/result_divide2.ml
87 article04/result_divide3.ml stejné jako result_divide1.fs, ovšem bez explicitního zápisu typů https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/result_divide3.ml
88 article04/result_divide4.ml stejné jako result_divide2.fs, ovšem bez explicitního zápisu typů https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/result_divide4.ml
       
89 article04/array_value.ml deklarace pole výčtem jeho prvků https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/array_value.ml
90 article04/array_make.ml funkce Array.make pro konstrukci pole https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/array_make.ml
91 article04/array_init1.ml inicializace prvků pole funkcí Array.init https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/array_init1.ml
92 article04/array_init2.ml inicializace prvků pole funkcí Array.init https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/array_init2.ml
93 article04/array_init3.ml inicializace prvků pole funkcí Array.init https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/array_init3.ml
94 article04/array_indexing.ml indexování prvků pole https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/array_indexing.ml
95 article04/array_mutation.ml mutace pole: modifikace hodnot jeho prvků https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/array_mutation.ml
       
96 article04/option_no_bind.ml zřetězení volání funkcí, které si předávají hodnoty typu Option – neidiomatické řešení https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/option_no_bind.ml
97 article04/option_bind.ml řešení založené na bind https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/option_bind.ml
98 article04/bind_infix_operator.ml funkce Option.bind zapsaná formou infixového operátoru https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/bind_infix_operator­.ml
99 article04/bind_infix_operator2.ml zřetězení funkcí s využitím Result.bind https://github.com/tisnik/ocaml-examples/tree/master/arti­cle04/bind_infix_operator.ml

19. Odkazy na Internetu

  1. General-Purpose, Industrial-Strength, Expressive, and Safe
    https://ocaml.org/
  2. OCaml playground
    https://ocaml.org/play
  3. Online Ocaml Compiler IDE
    https://www.jdoodle.com/compile-ocaml-online/
  4. Get Started – OCaml
    https://www.ocaml.org/docs
  5. Get Up and Running With OCaml
    https://www.ocaml.org/docs/up-and-running
  6. Better OCaml (Online prostředí)
    https://betterocaml.ml/?ver­sion=4.14.0
  7. OCaml file extensions
    https://blog.waleedkhan.name/ocaml-file-extensions/
  8. First thoughts on Rust vs OCaml
    https://blog.darklang.com/first-thoughts-on-rust-vs-ocaml/
  9. Standard ML of New Jersey
    https://www.smlnj.org/
  10. Programming Languages: Standard ML – 1 (a navazující videa)
    https://www.youtube.com/wat­ch?v=2sqjUWGGzTo
  11. 6 Excellent Free Books to Learn Standard ML
    https://www.linuxlinks.com/excellent-free-books-learn-standard-ml/
  12. SOSML: The Online Interpreter for Standard ML
    https://sosml.org/
  13. ML (Computer program language)
    https://www.barnesandnoble­.com/b/books/other-programming-languages/ml-computer-program-language/_/N-29Z8q8Zvy7
  14. Strong Typing
    https://perl.plover.com/y­ak/typing/notes.html
  15. What to know before debating type systems
    http://blogs.perl.org/user­s/ovid/2010/08/what-to-know-before-debating-type-systems.html
  16. Types, and Why You Should Care (Youtube)
    https://www.youtube.com/wat­ch?v=0arFPIQatCU
  17. DynamicTyping (Martin Fowler)
    https://www.martinfowler.com/bli­ki/DynamicTyping.html
  18. DomainSpecificLanguage (Martin Fowler)
    https://www.martinfowler.com/bli­ki/DomainSpecificLanguage­.html
  19. Language Workbenches: The Killer-App for Domain Specific Languages?
    https://www.martinfowler.com/ar­ticles/languageWorkbench.html
  20. Effective ML (Youtube)
    https://www.youtube.com/watch?v=-J8YyfrSwTk
  21. Why OCaml (Youtube)
    https://www.youtube.com/wat­ch?v=v1CmGbOGb2I
  22. Try OCaml
    https://try.ocaml.pro/
  23. CSE 341: Functions and patterns
    https://courses.cs.washin­gton.edu/courses/cse341/04wi/lec­tures/03-ml-functions.html
  24. Comparing Objective Caml and Standard ML
    http://adam.chlipala.net/mlcomp/
  25. What are the key differences between Standard ML and OCaml?
    https://www.quora.com/What-are-the-key-differences-between-Standard-ML-and-OCaml?share=1
  26. Cheat Sheets (pro OCaml)
    https://www.ocaml.org/doc­s/cheat_sheets.html
  27. Think OCaml: How to Think Like a (Functional) Programmer
    https://www.greenteapress­.com/thinkocaml/thinkocam­l.pdf
  28. The OCaml Language Cheat Sheet
    https://ocamlpro.github.io/ocaml-cheat-sheets/ocaml-lang.pdf
  29. Syllabus (FAS CS51)
    https://cs51.io/college/syllabus/
  30. Abstraction and Design In Computation
    http://book.cs51.io/
  31. Learn X in Y minutes Where X=Standard ML
    https://learnxinyminutes.com/doc­s/standard-ml/
  32. CSE307 Online – Summer 2018: Principles of Programing Languages course
    https://www3.cs.stonybrook­.edu/~pfodor/courses/summer/cse307­.html
  33. CSE307 Principles of Programming Languages course: SML part 1
    https://www.youtube.com/wat­ch?v=p1n0_PsM6hw
  34. CSE 307 – Principles of Programming Languages – SML
    https://www3.cs.stonybrook­.edu/~pfodor/courses/summer/CSE307/L01_SML­.pdf
  35. History of programming languages
    https://devskiller.com/history-of-programming-languages/
  36. History of programming languages (Wikipedia)
    https://en.wikipedia.org/wi­ki/History_of_programming_lan­guages
  37. The Evolution Of Programming Languages
    https://www.i-programmer.info/news/98-languages/8809-the-evolution-of-programming-languages.html
  38. Evoluce programovacích jazyků
    https://ccrma.stanford.edu/cou­rses/250a-fall-2005/docs/ComputerLanguagesChart.png
  39. Currying
    https://sw-samuraj.cz/2011/02/currying/
  40. Currying (Wikipedia)
    https://en.wikipedia.org/wi­ki/Currying
  41. Currying (Haskell wiki)
    https://wiki.haskell.org/Currying
  42. Haskell Curry
    https://en.wikipedia.org/wi­ki/Haskell_Curry
  43. Moses Schönfinkel
    https://en.wikipedia.org/wi­ki/Moses_Sch%C3%B6nfinkel
  44. So You Want to be a Functional Programmer (Part 1)
    https://cscalfani.medium.com/so-you-want-to-be-a-functional-programmer-part-1–1f15e387e536
  45. So You Want to be a Functional Programmer (Part 2)
    https://cscalfani.medium.com/so-you-want-to-be-a-functional-programmer-part-2–7005682cec4a
  46. So You Want to be a Functional Programmer (Part 3)
    https://cscalfani.medium.com/so-you-want-to-be-a-functional-programmer-part-3–1b0fd14eb1a7
  47. So You Want to be a Functional Programmer (Part 4)
    https://cscalfani.medium.com/so-you-want-to-be-a-functional-programmer-part-4–18fbe3ea9e49
  48. So You Want to be a Functional Programmer (Part 5)
    https://cscalfani.medium.com/so-you-want-to-be-a-functional-programmer-part-5-c70adc9cf56a
  49. So You Want to be a Functional Programmer (Part 6)
    https://cscalfani.medium.com/so-you-want-to-be-a-functional-programmer-part-6-db502830403
  50. Python to OCaml: Retrospective
    http://roscidus.com/blog/blog/2014/06/0­6/python-to-ocaml-retrospective/
  51. Why does Cambridge teach OCaml as the first programming language?
    https://www.youtube.com/wat­ch?v=6APBx0WsgeQ
  52. OCaml and 7 Things You Need To Know About It In 2021 | Functional Programming | Caml
    https://www.youtube.com/wat­ch?v=s0itOsgcf9Q
  53. OCaml 2021 – 25 years of OCaml
    https://www.youtube.com/watch?v=-u_zKPXj6mw
  54. Introduction | OCaml Programming | Chapter 1 Video 1
    https://www.youtube.com/wat­ch?v=MUcka_SvhLw&list=PLre5AT9JnKShBO­PeuiD9b-I4XROIJhkIU
  55. Functional Programming – What | OCaml Programming | Chapter 1 Video 2
    https://www.youtube.com/wat­ch?v=JTEwC3HihFc&list=PLre5AT9JnKShBO­PeuiD9b-I4XROIJhkIU&index=2
  56. Functional Programming – Why Part 1 | OCaml Programming | Chapter 1 Video 3
    https://www.youtube.com/wat­ch?v=SKr3ItChPSI&list=PLre5AT9JnKShBO­PeuiD9b-I4XROIJhkIU&index=3
  57. Functional Programming – Why Part 2 | OCaml Programming | Chapter 1 Video 4
    https://www.youtube.com/wat­ch?v=eNLm5Xbgmd0&list=PLre5AT9JnKShBO­PeuiD9b-I4XROIJhkIU&index=4
  58. OCaml | OCaml Programming | Chapter 1 Video 5
    https://www.youtube.com/watch?v=T-DIW1dhYzo&list=PLre5AT9JnKShBOPeuiD9b-I4XROIJhkIU&index=5
  59. Five Aspects of Learning a Programming Language | OCaml Programming | Chapter 2 Video 1
    https://www.youtube.com/wat­ch?v=A5IHFZtRfBs&list=PLre5AT9JnKShBO­PeuiD9b-I4XROIJhkIU&index=6
  60. Expressions | OCaml Programming | Chapter 2 Video 2
    https://www.youtube.com/watch?v=3fzrFY-2ZQ8&list=PLre5AT9JnKShBOPeuiD9b-I4XROIJhkIU&index=7
  61. If Expressions | OCaml Programming | Chapter 2 Video 3
    https://www.youtube.com/wat­ch?v=XJ6QPtlPD7s&list=PLre5AT9JnKShBO­PeuiD9b-I4XROIJhkIU&index=8
  62. Let Definitions | OCaml Programming | Chapter 2 Video 4
    https://www.youtube.com/wat­ch?v=eRnG4gwOTlI&list=PLre5AT9JnKShBO­PeuiD9b-I4XROIJhkIU&index=10
  63. Let Expressions | OCaml Programming | Chapter 2 Video 5
    https://www.youtube.com/wat­ch?v=ug3L97FXC6A&list=PLre5AT9JnKShBO­PeuiD9b-I4XROIJhkIU&index=10
  64. Variable Expressions and Scope | OCaml Programming | Chapter 2 Video 6
    https://www.youtube.com/wat­ch?v=_TpTC6eo34M&list=PLre5AT9JnKShBO­PeuiD9b-I4XROIJhkIU&index=11
  65. Scope and the Toplevel | OCaml Programming | Chapter 2 Video 7
    https://www.youtube.com/wat­ch?v=4SqMkUwakEA&list=PLre5AT9JnKShBO­PeuiD9b-I4XROIJhkIU&index=12
  66. Anonymous Functions | OCaml Programming | Chapter 2 Video 8
    https://www.youtube.com/wat­ch?v=JwoIIrj0bcM&list=PLre5AT9JnKShBO­PeuiD9b-I4XROIJhkIU&index=13
  67. Lambdas | OCaml Programming | Chapter 2 Video 9
    https://www.youtube.com/wat­ch?v=zHHCD7MOjmw&list=PLre5AT9JnKShBO­PeuiD9b-I4XROIJhkIU&index=15

Autor: Pavel Tišnovský   2023

Seriál: F# a OCaml

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.