Obsah
1. Definice uživatelských datových typů v jazyku OCaml
3. Funkce akceptující parametr typu záznam
4. Chování algoritmu typové inference v případě dvou struktur se shodnými prvky
8. Disjunktní sjednocení s prvky typu n-tice
9. Rekurzivní datová struktura: výraz
10. Rekurzivní generická datová struktura: strom
11. Třídy a objekty v ML jazycích
13. Metoda deklarovaná ve třídě
14. Konstrukce nové instance třídy realizovaná v metodě
15. Přetížení operátoru pro instance třídy
16. Deklarace přetíženého operátoru a automatické odvození typů operandů
18. Repositář s demonstračními příklady
1. Definice uživatelských datových typů v jazyku OCaml
Na tento týden vydaný článek o definicích uživatelských datových typů v jazyku F# dnes navážeme, protože si řekneme, jakým způsobem lze uživatelské datové typy definovat v programovacím jazyku OCaml. Uvidíme, že mezi oběma jazyky existují v této oblasti rozdíly, většinou však jen syntaktické (ovšem prozatím jsme se nevěnovali GADT, kde jsou již rozdíly dosti velké). Pro možnost rychlého vizuálního srovnání společných vlastností a rozdílů v syntaxi dnes zkusíme novinku – všechny příklady budou zapsány jak v jazyce OCaml, tak i v jazyce F# a budou zobrazeny vedle sebe, přičemž odpovídající si řádky budou skutečně zobrazeny na společném řádku (samozřejmě jen tam, kde je to možné – tedy až na poslední dva příklady).
Původní zdrojové kódy všech demonstračních příkladů tak nebude možné vykopírovat přímo z textu článku, ovšem samozřejmě jsou k dispozici zdrojové kódy všech příkladů uvedené v osmnácté kapitole.
2. Datový typ záznam (record)
Datový typ záznam (record), jenž byl z pohledu programovacího jazyka F# popsán zde, lze samozřejmě použít i v OCamlu. Mezi oběma jazyky však v této oblasti existují určité syntaktické rozdíly. Například v OCamlu jsou prvky popsány malými písmeny a jako oddělovače mezi prvky se jak v definici typu záznam, tak i při deklaraci hodnoty používají středníky. Rozdíly jsou dobře patrné při porovnání zdrojového kódu v OCamlu a F# (zobrazeno vedle sebe):
(* OCaml *) (* F# *)
type user = { type User =
id: int; { ID: int
name: string; Name: string
surname: string; Surname: string}
} ;;
let pepa = { let pepa =
id = 42; { ID = 42
name = "Josef"; Name = "Josef"
surname = "Vyskočil"} Surname = "Vyskočil"}
;;
pepa;; printf "%A\n" pepa
3. Funkce akceptující parametr typu záznam
Samozřejmě nám nic nebrání ve vytvoření funkce, která jako svůj parametr (či parametry) akceptuje hodnotu typu záznam, což je téma, kterému jsme se věnovali v této kapitole. Opět se podívejme na (nepatrné – syntaktické) rozdíly mezi jazyky OCaml a F# při definici a volání takové funkce:
(* OCaml *) (* F# *)
type user = { type User =
id: int; { ID: int
name: string; Name: string
surname: string; Surname: string
} ;; }
let print_user (u:user) = let print_user (x:User) =
Printf.printf "%s %s\n" u.name u.surname printf "%s %s" x.Name x.Surname
;;
let pepa = {
id = 42; let pepa =
name = "Josef"; { ID = 42
surname = "Vyskočil"} Name = "Josef"
;; Surname = "Vyskočil"}
print_user pepa;; print_user pepa
Oba programovací jazyky OCaml i F# používají velmi (podobně koncipovaný) sofistikovaný algoritmus pro typovou inferenci (type inference), který například dokáže doplnit typ parametrů do definované funkce, a to na základě jejího těla (nikoli na základě parametrů použitých při jejím volání). Opět se pouze podívejme na syntaktické rozdíly mezi oběma jazyky, protože z pohledu sémantiky jsme se tomuto tématu již věnovali:
(* OCaml *) (* F# *)
type user = { type User =
id: int; { ID: int
name: string; Name: string
surname: string; Surname: string
} ;; }
let print_user u = let print_user x =
Printf.printf "%s %s\n" u.name u.surname printf "%s %s" x.Name x.Surname
;;
let pepa = { let pepa =
id = 42; { ID = 42
name = "Josef"; Name = "Josef"
surname = "Vyskočil"} Surname = "Vyskočil"}
;;
print_user pepa;; print_user pepa
4. Chování algoritmu typové inference v případě dvou struktur se shodnými prvky
Ve čtvrté kapitole předchozího článku jsme si ukázali některá úskalí algoritmu typové inference. Ten jsme totiž „zmátli“ tím, že se ve funkci print_user pracuje jen s položkami Name a Surname, takže typová inference odvodí, že by se mohlo jednat o parametr typu UserWithoutID. Ovšem ve skutečnosti bude funkce volána s parametrem typu User, což vede k chybě při překladu. V programovacím jazyku OCaml dojde k naprosto stejné situaci (až – opět – na poněkud odlišnou syntaxi):
(* OCaml *) (* F# *)
type user = { type User =
id: int; { ID: int
name: string; Name: string
surname: string; Surname: string
} ;; }
type user_without_id = { type UserWithoutID =
name: string; { Name: string
surname: string; Surname: string
} ;;
let print_user u = let print_user x =
Printf.printf "%s %s\n" u.name u.surname printf "%s %s" x.Name x.Surname
;;
let pepa = { let pepa =
id = 42; { ID = 42
name = "Josef"; Name = "Josef"
surname = "Vyskočil"} Surname = "Vyskočil"}
;;
(* nefunkcni varianta *) (* nefunkcni varianta *)
print_user pepa;; print_user pepa
Řešením je explicitní specifikace typu parametru funkce print_user. Po této úpravě je již možné oba zdrojové kódy přeložit a spustit:
(* OCaml *) (* F# *)
type user = { type User =
id: int; { ID: int
name: string; Name: string
surname: string; Surname: string
} ;; }
type user_without_id = { type UserWithoutID =
name: string; { Name: string
surname: string; Surname: string
} ;; }
let print_user (u:user) = let print_user (x:User) =
Printf.printf "%s %s\n" u.name u.surname printf "%s %s" x.Name x.Surname
;;
let pepa = { let pepa =
id = 42; { ID = 42
name = "Josef"; Name = "Josef"
surname = "Vyskočil"} Surname = "Vyskočil"}
;;
(* funkcni varianta *) (* funkcni varianta *)
print_user pepa;; print_user pepa
5. Datový typ výčet (enum)
Připomeňme si, že v programovacím jazyku F# existuje datový typ výčet, v němž můžeme definovat jak prvky ve výčtu, tak i jejich hodnoty. V praxi je použití tohoto typu snadné, protože pouze postačuje specifikovat názvy a hodnoty jednotlivých prvků uložených ve výčtu. Hodnoty se získají snadno – opět se použije tečková notace, tedy podobně, jako je tomu u výše uvedených záznamů:
type Day = Po=1 | Ut=2 | St=3 | Ct=4 | Pa=5 | So=6 | Ne=7 let x = Day.St printf "%A\n" x
Zápis je možné provést i odlišným způsobem (který je sice delší, zato přehlednější):
type Day =
| Po=1
| Ut=2
| St=3
| Ct=4
| Pa=5
| So=6
| Ne=7
let x = Day.St
printf "%A\n" x
Tento datový typ v přesné podobě v OCamlu sice nenajdeme, ovšem kromě toho existuje i typ disjunktní sjednocení (discriminated union). V té nejjednodušší podobě může být tento typ definován pouhým výčtem možností a zde již v OCamlu najdeme stejný typ:
(* OCaml *) (* F# *) type day = Po | Ut | St | Ct | Pa | So | Ne;; type Day = Po | Ut | St | Ct | Pa | So | Ne let x = St;; let x = St x;; printf "%A\n" x
Popř.:
(* OCaml *) (* F# *)
type day = type Day =
| Po | Po
| Ut | Ut
| St | St
| Ct | Ct
| Pa | Pa
| So | So
| Ne;; | Ne
let x = St;; let x = St
x;; printf "%A\n" x
Opět se tedy jedná o nepatrnou změnu syntaxe, nikoli sémantiky.
6. Datový typ n-tice (tuple)
Datový typ n-tice byl z pohledu programovacího jazyka F# popsán minule v sedmé kapitole. V jazyku OCaml se používá naprosto stejná deklarace a i samotné hodnoty typu n-tice lze zpracovávat zcela stejným způsobem, včetně destructuringu (není divu, jedná se o dědictví ze společného prapředka – jazyka ML):
(* OCaml *) (* F# *) type rectangle = int * int;; type Rectangle = int * int let print_rectange r = let print_rectange r = let (width, height) = r in let (width, height) = r in Printf.printf "rect: %dx%d\n" width height printf "rect: %dx%d\n" width height ;; let r1 = (10, 20);; let r1 = (10, 20) print_rectange r1;; print_rectange r1
7. N-tice a pattern matching
Při čtení prvků z n-tice se často setkáme s využitím pattern matchingu, který v tomto případě může mít jen jedinou větev. Podívejme se na následující příklad, v němž z n-tice obsahující šířku a výšku obdélníku získáme obě délky v samostatných lokálních proměnných width a height. Samozřejmě opět porovnáme variantu napsanou v OCamlu s variantou naprogramovanou v jazyku F#:
(* OCaml *) (* F# *) type rectangle = int * int;; type Rectangle = int * int let print_rectange (r:rectangle) = let print_rectange (r : Rectangle) = match r with match r with | (width, height) -> Printf.printf "rect: %dx%d\n" width height | (width, height) -> printf "rect: %dx%d\n" width height ;; let r1 = (10, 20);; let r1 = (10, 20) print_rectange r1;; print_rectange r1
Mnohdy se taktéž setkáme s následujícím zápisem, který nás již připravuje na seznámení se s dalšími možnostmi zápisu disjunktního sjednocení (discriminated union):
(* OCaml *) (* F# *) type rectangle = R of int * int;; type Rectangle = R of int * int let print_rectange (r : rectangle) = let print_rectange (r : Rectangle) = match r with match r with | R(width, height) -> Printf.printf "rect: %dx%d\n" width height | R(width, height) -> printf "rect: %dx%d\n" width height ;; let r1 = R(10, 20);; let r1 = R(10, 20) print_rectange r1;; print_rectange r1
Shodné pojmenování typu a prvku sjednocení v jazyce F# většinou v OCaml nevyužijeme:
(* OCaml *) (* F# *) type rectangle = Rectangle of int * int;; type Rectangle = Rectangle of int * int let print_rectange (r:rectangle) = let print_rectange (r : Rectangle) = match r with match r with | Rectangle(width, height) -> Printf.printf "rect: %dx%d\n" width height | Rectangle(width, height) -> printf "rect: %dx%d\n" width height ;; let r1 = Rectangle(10, 20);; let r1 = Rectangle(10, 20) print_rectange r1;; print_rectange r1
8. Disjunktní sjednocení s prvky typu n-tice
Minule jsme si taktéž ukázali velmi důležitý rys ML jazyků – možnost definovat typ reprezentující několik různých hodnot, které samy o sobě nejsou stejného typu. Příkladem je typ nazvaný Shape, který ve skutečnosti znamená, že buď pracujeme s hodnotou typu Rectangle nebo hodnotou typu Circle. Použijeme zde disjunktní sjednocení, které se v obou jazycích, tj. jak v OCamlu, tak i v jazyku F#, zapisuje prakticky totožným způsobem:
(* OCaml *) (* F# *) type shape = Rectangle of int * int | Circle of int;; type Shape = Rectangle of int * int | Circle of int let print_shape (s : shape) = let print_shape (s : Shape) = match s with match s with | Circle r -> Printf.printf "circle: %d\n" r | Circle r -> printf "circle: %d\n" r | Rectangle (width, height) -> Printf.printf "rect: %dx%d\n" width height | Rectangle (width, height) -> printf "rect: %dx%d\n" width height ;; let r1 = Rectangle (10, 20);; let r1 = Rectangle (10, 20) let c = Circle 100;; let c = Circle 100 print_shape r1;; print_shape r1 print_shape c;; print_shape c
V praxi se velmi často setkáme s tím, že deklarace typu Shape/shape se zapisuje na více řádcích, aby tak po vizuální stránce odpovídala blokům match, v nichž s hodnotami tohoto typu pracujeme (každý konkrétní typ pak začíná řádkem začínajícím znakem |):
(* OCaml *) (* F# *) type shape = type Shape = | Rectangle of int * int | Circle of int | Circle of int;; | Rectangle of int * int let print_shape (s : shape) = let print_shape (s : Shape) = match s with match s with | Circle r -> Printf.printf "circle: %d\n" r | Circle r -> printf "circle: %d\n" r | Rectangle (width, height) -> Printf.printf "rect: %dx%d\n" width height | Rectangle (width, height) -> printf "rect: %dx%d\n" width height ;; let r1 = Rectangle (10, 20);; let r1 = Rectangle (10, 20) let c = Circle 100;; let c = Circle 100 print_shape r1;; print_shape r1 print_shape c;; print_shape c
9. Rekurzivní datová struktura: výraz
Disjunktní sjednocení zkombinované s n-ticemi ve skutečnosti představuje velmi silný rys programovacích jazyků F# i OCaml. Tento rys je navíc umocněn tím, že je možné definovat i rekurzivní datový typ, kdy jedna z položek sjednocení je typem obsahujícím samotné sjednocení. Příkladem je reprezentace výrazu (expression); ostatně právě OCaml se často používá při implementaci překladačů, kde se s podobnými objekty můžeme velmi často setkat. Zápis takového rekurzivního typu je v obou jazycích prakticky totožný:
(* OCaml *) (* F# *) type expr = type expr = | Plus of expr * expr (* a + b *) | Plus of expr * expr (* a + b *) | Minus of expr * expr (* a - b *) | Minus of expr * expr (* a - b *) | Times of expr * expr (* a * b *) | Times of expr * expr (* a * b *) | Divide of expr * expr (* a / b *) | Divide of expr * expr (* a / b *) | Var of string | Var of string ;; let x = Times (Var "n", Plus (Var "x", Var "y"));; let x = Times (Var "n", Plus (Var "x", Var "y")) x;; printf "%A\n" x
10. Rekurzivní generická datová struktura: strom
Možnosti typového systému jazyků OCaml a F# jdou ve skutečnosti ještě dále, protože můžeme vytvořit generický rekurzivní datový typ. Tentokrát se bude jednat o datový typ představující strom (přesněji řečeno binární strom), což je pochopitelně rekurzivní datová struktura. Povšimněte si, že existují dva typy prvků (uzlů). Prázdný (koncový uzel) nebo uzel představovaný n-ticí obsahující levý podstrom, hodnotu uloženou v uzlu a pravý podstrom (samozřejmě, že podstromy mohou být prázdné, takže se může jednat o list stromu). V jazyku OCaml se v tomto případě používá poněkud odlišný zápis – podle mého skromného názoru čitelnější, než v případě F#, ovšem F# je v tomto ohledu poplatný jazykům odvozeným od C++ či Javy:
(* OCaml *) (* F# *) type 'a tree = type Tree<'a> = | E | E | T of 'a tree * 'a * 'a tree | T of Tree<'a> * 'a * Tree<'a> ;; let t1 = T(E, "foo", E);; let t1 = T(E, "foo", E) let t2 = T(T(E, "foo", E), "bar", T(E, "baz", E));; let t2 = T(T(E, "foo", E), "bar", T(E, "baz", E)) t1;; printf "%A\n" t1 t2;; printf "%A\n" t2
11. Třídy a objekty v ML jazycích
Písmeno „O“ v názvu jazyka OCaml znamená „objective“, což nám prozrazuje, že tento programovací jazyk nabízí programátorům možnost práce s objekty (jejichž typem je třída). Zatímco v OCamlu se jednalo o více či méně užitečnou „úlitbu“ dobovým požadavkům, je podpora objektově orientovaného programování v jazyku F# prakticky nutností, protože programy psané v tomto jazyku musí spolupracovat s dalšími jazyky v ekosystému .NET. Z tohoto důvodu si v dalších kapitolách připomeneme základy OOP v jazyku F# i to, jak se podobné koncepty realizují v jazyku OCaml (jenž je starší a nebyl navržen tak, aby se podobal například C++).
12. Deklarace třídy
Podívejme se nyní na deklaraci jednoduché třídy Rectangle, jejíž instance budou mít dva atributy nazvané X a Y (rozměry v jednotlivých osách) a s konstruktorem, který akceptuje dva parametry typu int (výchozí hodnoty rozměrů). Zde se již syntaxe obou jazyků poměrně významně odlišuje:
(* OCaml *) (* F# *)
class rectangle (x:int) (y:int) = type Rectangle(x: int, y: int) =
object (self) member this.X = x
val x = x member this.Y = y
val y = y
end;;
let r1 = new rectangle 10 20;; let r1 = Rectangle(10, 20)
r1;; printf "%A\n" r1
13. Metoda deklarovaná ve třídě
Do deklarace třídy můžeme přidat i metody. Například se může jednat o metodu nazvanou Print (v OCamlu print), která vytiskne jak název objektu, tak i jeho atributy. Zde již narazíme na dnes možná poněkud zvláštní způsob zápisu volání metody v OCamlu, kdy se namísto tečkové notace používá křížek (ale samozřejmě je snadné si na to zvyknout):
(* OCaml *) (* F# *)
class rectangle (x:int) (y:int) = type Rectangle(x: int, y: int) =
object (self) member this.X = x
val x = x member this.Y = y
val y = y member this.Print() =
method print = Printf.printf "Rectangle: %dx%d\n" x y printf "Rectangle: %dx%d\n" x y
end;;
let r1 = new rectangle 10 20;; let r1 = Rectangle(10, 20)
r1#print;; r1.Print()
14. Konstrukce nové instance třídy realizovaná v metodě
Ve shodně očíslované čtrnácté kapitole jsme si minule řekli, že se relativně často setkáme s tím, že nějaká metoda má změnit stav objektu, tj. vlastně hodnoty jeho atributů. To lze samozřejmě zařídit tak, že se příslušné atributy deklarují takovým způsobem, aby byly měnitelné. Ovšem mnohdy je výhodnější použít odlišný přístup – taková metoda bude vracet nový objekt, ovšem již se změněným stavem. Příkladem může být požadavek na změnu velikosti obdélníka, tedy získání obdélníka, jehož rozměry na x-ové a y-ové ose budou zvětšeny nebo zmenšeny o nějaké hodnoty dx a dy. Pro tento účel lze deklarovat metodu Enlarge, která vrací novou instanci Rectangle (ale stávající instanci nijak nemění).
Realizace v obou porovnávaných jazycích by mohla vypadat následovně:
(* OCaml *) (* F# *)
class rectangle (x:int) (y:int) = type Rectangle(x: int, y: int) =
object (self) member this.X = x
val x = x member this.Y = y
val y = y member this.Print() =
method print = Printf.printf "Rectangle: %dx%d\n" x y printf "Rectangle: %dx%d\n" this.X this.Y
method enlarge (xd:int) (yd:int) = new rectangle (x+xd) (y+yd) member this.Enlarge(dx, dy) =
end;; Rectangle(this.X + dx, this.Y + dy)
let r1 = new rectangle 10 20;; let r1 = Rectangle(10, 20)
let r2 = r1#enlarge 1 2;; let r2 = r1.Enlarge(1, 2)
r1#print;; r1.Print()
r2#print;; r2.Print()
15. Přetížení operátoru pro instance třídy
V dnešním posledním demonstračním příkladu, v němž budeme porovnávat sémantické a syntaktické shody a rozdíly mezi programovacími jazyky OCaml a F#, si ukážeme definici přetíženého operátoru pro instance třídy Vector, resp. vector. V případě jazyka OCaml je tento operátor přetížen na úrovni modulu, zatímco v jazyku F# můžeme operátor definovat jako statickou metodu třídy Vector. Výsledek ovšem bude stejný – možnost sčítat vektory tak, jak je to běžné v matematice, tedy s využitím k tomu určeného (přetíženého) operátoru +:
(* OCaml *) (* F# *)
class vector (x:int) (y:int) = type Vector(x: int, y: int) =
object (self) member this.X = x
val x = x member this.Y = y
val y = y member this.Print() =
method print = Printf.printf "Vector: %dx%d\n" x y printf "Vector: %dx%d\n" this.X this.Y
method get_x = x
method get_y = y
end;;
let(+) (a: vector) (b: vector) = static member (+) (a : Vector, b : Vector) =
new vector (a#get_x+b#get_x) (a#get_y+b#get_y);; Vector(a.X + b.X, a.Y + b.Y)
let v1 = new vector 10 20;; let v1 = Vector(10, 20)
v1#print;; v1.Print()
let v2 = new vector 1 2;; let v2 = Vector(1, 2)
v2#print;; v2.Print()
let v3 = v1 + v2;; let v3 = v1 + v2
v3#print;; v3.Print()
let(+@) (a: vector) (b: vector) = new vector (a#get_x+b#get_x) (a#get_y+b#get_y);;
16. Deklarace přetíženého operátoru a automatické odvození typů operandů
Díky algoritmu typové inference je možné vynechat explicitní určení typů parametrů pro nově definovaný (či přetížený) operátor. Výsledkem bude kratší programový kód, který však od programátora již vyžaduje znalost kontextu:
class vector x y =
object (self)
val x = x
val y = y
method print = Printf.printf "Vector: %dx%d\n" x y
method get_x = x
method get_y = y
end;;
let(+?) a b = new vector (a#get_x+b#get_x) (a#get_y+b#get_y);;
let v1 = new vector 10 20;;
v1#print;;
let v2 = new vector 1 2;;
v2#print;;
let v3 = v1 +? v2;;
v3#print;;
17. Kam dál? GADT
Až doposud bylo patrné, že jazyky F# a OCaml mají prakticky totožnou sémantiku a většinou i velmi podobnou syntaxi. Ovšem právě v oblasti typových systémů se začínají oba jazyky postupně rozcházet. Je tomu tak především proto, že v OCamlu lze od verze 4.00 používat takzvaný GADT neboli Generalized algebraic data type. Jedná se o velmi zajímavý a užitečný koncept, kterému se budeme věnovat v samostatném článku (resp. s velkou pravděpodobností ve dvou článcích). Ovšem stále nám zbývá popis společných vlastností obou jazyků. Například jsme se doposud nezabývali všemi řídicími konstrukcemi jazyků F# a OCaml atd.
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:
19. Odkazy na Internetu
- General-Purpose, Industrial-Strength, Expressive, and Safe
https://ocaml.org/ - OCaml playground
https://ocaml.org/play - Online Ocaml Compiler IDE
https://www.jdoodle.com/compile-ocaml-online/ - Get Started – OCaml
https://www.ocaml.org/docs - Get Up and Running With OCaml
https://www.ocaml.org/docs/up-and-running - Better OCaml (Online prostředí)
https://betterocaml.ml/?version=4.14.0 - OCaml file extensions
https://blog.waleedkhan.name/ocaml-file-extensions/ - First thoughts on Rust vs OCaml
https://blog.darklang.com/first-thoughts-on-rust-vs-ocaml/ - Standard ML of New Jersey
https://www.smlnj.org/ - Programming Languages: Standard ML – 1 (a navazující videa)
https://www.youtube.com/watch?v=2sqjUWGGzTo - 6 Excellent Free Books to Learn Standard ML
https://www.linuxlinks.com/excellent-free-books-learn-standard-ml/ - SOSML: The Online Interpreter for Standard ML
https://sosml.org/ - ML (Computer program language)
https://www.barnesandnoble.com/b/books/other-programming-languages/ml-computer-program-language/_/N-29Z8q8Zvy7 - Strong Typing
https://perl.plover.com/yak/typing/notes.html - What to know before debating type systems
http://blogs.perl.org/users/ovid/2010/08/what-to-know-before-debating-type-systems.html - Types, and Why You Should Care (Youtube)
https://www.youtube.com/watch?v=0arFPIQatCU - DynamicTyping (Martin Fowler)
https://www.martinfowler.com/bliki/DynamicTyping.html - DomainSpecificLanguage (Martin Fowler)
https://www.martinfowler.com/bliki/DomainSpecificLanguage.html - Language Workbenches: The Killer-App for Domain Specific Languages?
https://www.martinfowler.com/articles/languageWorkbench.html - Effective ML (Youtube)
https://www.youtube.com/watch?v=-J8YyfrSwTk - Why OCaml (Youtube)
https://www.youtube.com/watch?v=v1CmGbOGb2I - Try OCaml
https://try.ocaml.pro/ - CSE 341: Functions and patterns
https://courses.cs.washington.edu/courses/cse341/04wi/lectures/03-ml-functions.html - Comparing Objective Caml and Standard ML
http://adam.chlipala.net/mlcomp/ - 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 - Cheat Sheets (pro OCaml)
https://www.ocaml.org/docs/cheat_sheets.html - Think OCaml: How to Think Like a (Functional) Programmer
https://www.greenteapress.com/thinkocaml/thinkocaml.pdf - The OCaml Language Cheat Sheet
https://ocamlpro.github.io/ocaml-cheat-sheets/ocaml-lang.pdf - Syllabus (FAS CS51)
https://cs51.io/college/syllabus/ - Abstraction and Design In Computation
http://book.cs51.io/ - Learn X in Y minutes Where X=Standard ML
https://learnxinyminutes.com/docs/standard-ml/ - CSE307 Online – Summer 2018: Principles of Programing Languages course
https://www3.cs.stonybrook.edu/~pfodor/courses/summer/cse307.html - CSE307 Principles of Programming Languages course: SML part 1
https://www.youtube.com/watch?v=p1n0_PsM6hw - CSE 307 – Principles of Programming Languages – SML
https://www3.cs.stonybrook.edu/~pfodor/courses/summer/CSE307/L01_SML.pdf - SML, Some Basic Examples
https://cs.fit.edu/~ryan/sml/intro.html - History of programming languages
https://devskiller.com/history-of-programming-languages/ - History of programming languages (Wikipedia)
https://en.wikipedia.org/wiki/History_of_programming_languages - Jemný úvod do rozsáhlého světa jazyků LISP a Scheme
https://www.root.cz/clanky/jemny-uvod-do-rozsahleho-sveta-jazyku-lisp-a-scheme/ - The Evolution Of Programming Languages
https://www.i-programmer.info/news/98-languages/8809-the-evolution-of-programming-languages.html - Evoluce programovacích jazyků
https://ccrma.stanford.edu/courses/250a-fall-2005/docs/ComputerLanguagesChart.png - Poly/ML Homepage
https://polyml.org/ - PolyConf 16: A brief history of F# / Rachel Reese
https://www.youtube.com/watch?v=cbDjpi727aY - Programovací jazyk Clojure 18: základní techniky optimalizace aplikací
https://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/ - Moscow ML Language Overview
https://itu.dk/people/sestoft/mosml/mosmlref.pdf - ForLoops
http://mlton.org/ForLoops - Funkcionální dobrodružství v JavaScriptu
https://blog.kolman.cz/2015/12/funkcionalni-dobrodruzstvi-v-javascriptu.html - Recenze knihy Functional Thinking (Paradigm over syntax)
https://www.root.cz/clanky/recenze-knihy-functional-thinking-paradigm-over-syntax/ - Currying
https://sw-samuraj.cz/2011/02/currying/ - Používání funkcí v F#
https://docs.microsoft.com/cs-cz/dotnet/fsharp/tutorials/using-functions - Funkce vyššího řádu
http://naucte-se.haskell.cz/funkce-vyssiho-radu - Currying (Wikipedia)
https://en.wikipedia.org/wiki/Currying - Currying (Haskell wiki)
https://wiki.haskell.org/Currying - Haskell Curry
https://en.wikipedia.org/wiki/Haskell_Curry - Moses Schönfinkel
https://en.wikipedia.org/wiki/Moses_Sch%C3%B6nfinkel - .NET framework
https://dotnet.microsoft.com/en-us/ - F# – .NET Blog
https://devblogs.microsoft.com/dotnet/category/fsharp/ - Playground: OCaml
https://ocaml.org/play - The F# Survival Guide
https://web.archive.org/web/20110715231625/http://www.ctocorner.com/fsharp/book/default.aspx - Object-Oriented Programming — The Trillion Dollar Disaster
https://betterprogramming.pub/object-oriented-programming-the-trillion-dollar-disaster-92a4b666c7c7 - Goodbye, Object Oriented Programming
https://cscalfani.medium.com/goodbye-object-oriented-programming-a59cda4c0e53 - 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 - 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 - 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 - 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 - 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 - 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 - Don Syme
https://en.wikipedia.org/wiki/Don_Syme - Python to OCaml: Retrospective
http://roscidus.com/blog/blog/2014/06/06/python-to-ocaml-retrospective/ - Why does Cambridge teach OCaml as the first programming language?
https://www.youtube.com/watch?v=6APBx0WsgeQ - OCaml and 7 Things You Need To Know About It In 2021 | Functional Programming | Caml
https://www.youtube.com/watch?v=s0itOsgcf9Q - OCaml 2021 – 25 years of OCaml
https://www.youtube.com/watch?v=-u_zKPXj6mw - Introduction | OCaml Programming | Chapter 1 Video 1
https://www.youtube.com/watch?v=MUcka_SvhLw&list=PLre5AT9JnKShBOPeuiD9b-I4XROIJhkIU - Functional Programming – What | OCaml Programming | Chapter 1 Video 2
https://www.youtube.com/watch?v=JTEwC3HihFc&list=PLre5AT9JnKShBOPeuiD9b-I4XROIJhkIU&index=2 - Functional Programming – Why Part 1 | OCaml Programming | Chapter 1 Video 3
https://www.youtube.com/watch?v=SKr3ItChPSI&list=PLre5AT9JnKShBOPeuiD9b-I4XROIJhkIU&index=3 - Functional Programming – Why Part 2 | OCaml Programming | Chapter 1 Video 4
https://www.youtube.com/watch?v=eNLm5Xbgmd0&list=PLre5AT9JnKShBOPeuiD9b-I4XROIJhkIU&index=4 - OCaml | OCaml Programming | Chapter 1 Video 5
https://www.youtube.com/watch?v=T-DIW1dhYzo&list=PLre5AT9JnKShBOPeuiD9b-I4XROIJhkIU&index=5 - Five Aspects of Learning a Programming Language | OCaml Programming | Chapter 2 Video 1
https://www.youtube.com/watch?v=A5IHFZtRfBs&list=PLre5AT9JnKShBOPeuiD9b-I4XROIJhkIU&index=6 - Expressions | OCaml Programming | Chapter 2 Video 2
https://www.youtube.com/watch?v=3fzrFY-2ZQ8&list=PLre5AT9JnKShBOPeuiD9b-I4XROIJhkIU&index=7 - If Expressions | OCaml Programming | Chapter 2 Video 3
https://www.youtube.com/watch?v=XJ6QPtlPD7s&list=PLre5AT9JnKShBOPeuiD9b-I4XROIJhkIU&index=8 - Let Definitions | OCaml Programming | Chapter 2 Video 4
https://www.youtube.com/watch?v=eRnG4gwOTlI&list=PLre5AT9JnKShBOPeuiD9b-I4XROIJhkIU&index=10 - Let Expressions | OCaml Programming | Chapter 2 Video 5
https://www.youtube.com/watch?v=ug3L97FXC6A&list=PLre5AT9JnKShBOPeuiD9b-I4XROIJhkIU&index=10 - Variable Expressions and Scope | OCaml Programming | Chapter 2 Video 6
https://www.youtube.com/watch?v=_TpTC6eo34M&list=PLre5AT9JnKShBOPeuiD9b-I4XROIJhkIU&index=11 - Scope and the Toplevel | OCaml Programming | Chapter 2 Video 7
https://www.youtube.com/watch?v=4SqMkUwakEA&list=PLre5AT9JnKShBOPeuiD9b-I4XROIJhkIU&index=12 - Anonymous Functions | OCaml Programming | Chapter 2 Video 8
https://www.youtube.com/watch?v=JwoIIrj0bcM&list=PLre5AT9JnKShBOPeuiD9b-I4XROIJhkIU&index=13 - Lambdas | OCaml Programming | Chapter 2 Video 9
https://www.youtube.com/watch?v=zHHCD7MOjmw&list=PLre5AT9JnKShBOPeuiD9b-I4XROIJhkIU&index=15 - Operators
https://ocaml.org/docs/operators - Operator overloading
https://en.wikipedia.org/wiki/Operator_overloading - Generalized algebraic data type
https://en.wikipedia.org/wiki/Generalized_algebraic_data_type