Obsah
1. Definice uživatelských datových typů v jazyku F#
3. Typová inference při definici funkcí
4. Chování algoritmu typové inference v případě dvou struktur se shodnými prvky
6. Datový typ disjunktní sjednocení (discriminated union)
9. Od n-tic k sofistikovanějším disjunktním sjednocením
10. Disjunktní sjednocení s prvky typu n-tice
13. Lokální symbol v deklaraci třídy
14. Konstrukce nové instance třídy realizovaná v metodě
15. Operátory definované v rámci uživatelského datového typu
17. Operátor upcast: přetypování na předka
18. Repositář s demonstračními příklady
1. Definice uživatelských datových typů v jazyku F#
Prozatím jsme si v seriálu o programovacích jazycích OCaml a F# do značné míry vystačili se základními datovými typy programovacího jazyka F# (resp. OCamlu). Ovšem velká síla těchto dvou programovacích jazyků spočívá v principu deklarace nových datových typů. Podporován je takzvaný algebraický typový systém (který byl v OCaml dále rozšířen), ale například i možnost deklarace tříd a specifikace jejich hierarchie (i když v F# ani v OCamlu není tak velký „tlak“ na použití tříd, a to právě díky možnostem poskytovaných ostatními datovými typy).
V mnoha demonstračních příkladech uvedených v navazujících kapitolách se navíc setkáme s tím, že operace nad různými datovými typy jsou prováděny s využitím pattern matchingu, přičemž stojí za povšimnutí, že konstrukce hodnoty určitého datového typu vypadá po syntaktické stránce prakticky stejně, jako dekonstrukce hodnoty na její prvky v bloku match. To je poměrně typický rys pattern matchingu, který byl do určité míry přejat i do dalších programovacích jazyků, které dnes pattern matching podporují.
2. Datový typ záznam (record)
V mnoha oblastech se používá datový typ záznam (record). V záznamech mohou být uloženy prvky (resp. přesněji řečeno složky) libovolného typu, ovšem na rozdíl od již popsaných n-tic (tuple) jsou tyto prvky pojmenovány a lze k nim přistupovat s využitím známé tečkové notace (tečka přitom není v tomto kontextu operátor, ale znak se speciálním významem – special symbol). U záznamů je, na rozdíl od n-tic, seznamů či polí, již nutné definovat nový datový typ s explicitním určením typů jednotlivých položek.
Jak práce se seznamy vypadá v praxi? Datový typ User se třemi složkami může být definován následujícím způsobem:
type User = { ID: int Name: string Surname: string}
Vytvoření proměnné s hodnotou tohoto typu se zapisuje stylem, který je ukázán pod tímto odstavcem:
let pepa = { ID = 42 Name = "Josef" Surname = "Vyskočil"} printf "%A\n" pepa
Samozřejmě si můžeme v případě potřeby nadeklarovat i funkci (například pojmenovanou print_name), která akceptuje parametr typu záznam. V této funkci navíc budeme přistupovat ke složkám záznamu s využitím dnes již téměř univerzální „tečkové notace“:
type User = { ID: int Name: string Surname: string} let print_name (x:User) = printf "%s %s" x.Name x.Surname let pepa = { ID = 42 Name = "Josef" Surname = "Vyskočil"} print_name pepa
3. Typová inference při definici funkcí
Již několikrát jsme se v tomto seriálu setkali s tím, že programovací jazyky OCaml a F# používají 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. Můžeme si tedy vyzkoušet upravit funkci z předchozí kapitoly. Původní tvar této funkce explicitně specifikoval typ parametrů:
let print_name (x:User) = printf "%s %s" x.Name x.Surname
Ovšem v tomto případě (až na výjimky zmíněné níže) můžeme funkci napsat jednodušeji a přitom bude stále silně typovaná:
let print_name x = printf "%s %s" x.Name x.Surname
Následující skript je korektní a plně funkční:
type User = { ID: int Name: string Surname: string} let print_name x = printf "%s %s" x.Name x.Surname let pepa = { ID = 42 Name = "Josef" Surname = "Vyskočil"} print_name pepa
4. Chování algoritmu typové inference v případě dvou struktur se shodnými prvky
Při pohledu na demonstrační příklad z předchozí kapitoly by se mohlo zdát, že algoritmus pro typovou inferenci odvozuje typ parametrů funkcí podle toho, s jakými skutečnými hodnotami jsou funkce volány. Ovšem snadno se můžeme přesvědčit, že to není (zcela) pravda. V dalším skriptu jsou deklarovány dva datové typy User a UserWithoutID, které mají položky se stejným jménem Name a Surname. Ve funkci print_name se 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:
type User = { ID: int Name: string Surname: string} type UserWithoutID = { Name: string Surname: string} let print_name x = printf "%s %s" x.Name x.Surname let pepa = { ID = 42 Name = "Josef" Surname = "Vyskočil"} (* nefunkcni varianta *) print_name pepa
Řešením je explicitní specifikace typu parametru funkce print_name (viz podtržená část kódu):
type User = { ID: int Name: string Surname: string} type UserWithoutID = { Name: string Surname: string} let print_name (x:User) = printf "%s %s" x.Name x.Surname let pepa = { ID = 42 Name = "Josef" Surname = "Vyskočil"} (* funkcni varianta *) print_name pepa
5. Datový typ výčet (enum)
Dalším způsobem, jak v programovacím jazyku F# definovat datový typ, je definice výčtu (enum). Je to 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:
type Day = Po=1 | Ut=2 | St=3 | Ct=4 | Pa=5 | So=6 | Ne=7 let x = Day.St printf "%A\n" x
Alternativní způsob zápisu výčtu vypadá takto (použitý je pro delší názvy či hodnoty):
type Day = | Po=1 | Ut=2 | St=3 | Ct=4 | Pa=5 | So=6 | Ne=7 let x = Day.St printf "%A\n" x
6. Datový typ disjunktní sjednocení (discriminated union)
Velmi důležitým datovým typem je v programovacím jazyku F# typ nazývaný disjunktní sjednocení neboli discriminated union. V té nejjednodušší podobě může být tento typ definován pouhým výčtem možností:
type Day = Po | Ut | St | Ct | Pa | So | Ne let x = St printf "%A\n" x
Alternativní způsob zápisu:
type Day = | Po | Ut | St | Ct | Pa | So | Ne let x = St printf "%A\n" x
Až doposud by se mohlo zdát, že se vlastně jedná o typ výčet, ovšem možnosti disjunktního sjednocení jsou mnohem větší. Již jsme se setkali s typy Option a Result:
type Option<'a> = | Some of 'a | None
a:
type Result<'T,'TError> = | Ok of ResultValue:'T | Error of ErrorValue:'TError
Další podobné konstrukce budou uvedeny v navazujících kapitolách.
7. N-tice
O tom, jak se vytváří hodnoty typu n-tice (tuple) jsme si již v tomto seriálu říkali. Postačuje napsat například:
let x = (1, 1.5, "foo", (1,2)) ;;
přičemž výsledkem bude hodnota typu:
int * float * string * (int * int)
Za povšimnutí stojí především zápis * mezi typy jednotlivých prvků, což napovídá, že na rozdíl předchozího typu se v tomto případě nejedná o součtový datový typ, ale naopak o součinový datový typ (existuje jen jedna varianta).
Konkrétní typ n-tice si samozřejmě můžeme snadno zadefinovat a může se tak jednat o určitou alternativu k záznamům. V dalším příkladu je ukázán neidiomatický způsob získání jednotlivých hodnot prvků z n-tice s využitím vnořeného bloku let:
type Rectangle = int * int let print_rectange r = let (width, height) = r in printf "rect: %dx%d\n" width height let r1 = (10, 20) print_rectange r1
8. N-tice a pattern matching
Při čtení prvků z n-tice se mnohdy 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:
type Rectangle = int * int let print_rectange (r : Rectangle) = match r with | (width, height) -> printf "rect: %dx%d\n" width height let r1 = (10, 20) print_rectange r1
9. Od n-tic k sofistikovanějším disjunktním sjednocením
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) – viz další kapitoly:
type Rectangle = R of int * int let print_rectange (r : Rectangle) = match r with | R(width, height) -> printf "rect: %dx%d\n" width height let r1 = R(10, 20) print_rectange r1
Můžeme dokonce rozlišit mezi jménem typu a jménem položky – identifikátory jsou stejné, ovšem mají odlišný význam:
type Rectangle = Rectangle of int * int let print_rectange (r : Rectangle) = match r with | Rectangle(width, height) -> printf "rect: %dx%d\n" width height let r1 = Rectangle(10, 20) print_rectange r1
10. Disjunktní sjednocení s prvky typu n-tice
Nyní se dostáváme k velmi důležitému rysu typového systému programovacího jazyka F#. Můžeme totiž vytvořit nový datový typ umožňující reprezentaci hodnoty, která sama o sobě může být různého typu. Typickým příkladem je typ nazvaný Shape, který je disjunktním sjednocením jednotlivých konkrétních typů (tvarů), například obdélníku a kružnice:
type Shape = Rectangle of int * int | Circle of int
Deklarace proměnných typu Shape:
let r1 = Rectangle (10, 20) let c = Circle 100
Přidejme si ještě funkci, která bude hodnoty typu Shape tisknout. Pro rozlišení konkrétního typu hodnoty použijeme pattern matching a (opět) si povšimněte, že zápis na levé straně šipky syntakticky přesně odpovídá deklaraci hodnoty uvedené výše:
type Shape = Rectangle of int * int | Circle of int let print_shape (s : Shape) = match s with | Circle r -> printf "circle: %d\n" r | Rectangle (width, height) -> printf "rect: %dx%d\n" width height let r1 = Rectangle (10, 20) let c = Circle 100 print_shape r1 print_shape c
Navíc v tomto případě můžeme typ parametru funkce print_shape vynechat; tuto práci za nás udělá algoritmus pro odvození typů (typovou inferenci):
let print_shape s = match s with | Circle r -> printf "circle: %d\n" r | Rectangle (width, height) -> printf "rect: %dx%d\n" width height
V praxi se namísto zápisu:
type Shape = Rectangle of int * int | Circle of int
mnohdy setkáme s rozdělením na více řádky:
type Shape = | Circle of int | Rectangle of int * int let print_shape (s : Shape) = match s with | Circle r -> printf "circle: %d\n" r | Rectangle (width, height) -> printf "rect: %dx%d\n" width height let r1 = Rectangle (10, 20) let c = Circle 100 print_shape r1 print_shape c
11. Rekurzivní a generické datové typy
Disjunktní sjednocení zkombinované s n-ticemi ve skutečnosti představuje velmi silný rys programovacího jazyka F#. Ten 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í. Zní to jako zcela akademický problém? Podívejme se na příklad definice datového typu, který reprezentuje výrazy (ne tak, jak jsou zapsány v kódu, ale jejich inherentní rekurzivní podobu). Nový datový typ se jmenuje expression a jedná se o disjunktní sjednocení pěti možností – čtyř podvýrazů a názvu proměnných:
type expression = | Plus of expression * expression (* a + b *) | Minus of expression * expression (* a - b *) | Times of expression * expression (* a * b *) | Divide of expression * expression (* a / b *) | Var of string let x = Times (Var "n", Plus (Var "x", Var "y")) printf "%A\n" x
Ovšem možnosti datového systému jazyka F# jdou ještě dále, protože můžeme vytvořit generický datový typ. Tentokrát se bude jednat o datový typ představující strom, což je rekurzivní 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):
type Tree<'a> = | E | T of Tree<'a> * 'a * Tree<'a> let t1 = T(E, "foo", E) let t2 = T(T(E, "foo", E), "bar", T(E, "baz", E)) printf "%A\n" t1 printf "%A\n" t2
12. Datový typ třída (class)
Jazyk F# je odvozen z jazyka OCaml, kde „O“ znamená „objective“. Navíc musí F# spolupracovat s dalšími jazyky v ekosystému .NET. Z obou vlastností vyplývá, že F# vlastně musí podporovat objektově orientované programování, konkrétně OOP založené na třídách (i když samotný F# je spíše funkcionálním jazykem). Podívejme se nyní na deklaraci jednoduché třídy, jejíž instance budou mít dva atributy X a Y (rozměry v jednotlivých osách) a s konstruktorem, který akceptuje dva parametry typu int (výchozí hodnoty rozměrů):
type Rectangle(x: int, y: int) = member this.X = x member this.Y = y (* konstrukce objektu typu Rectangle *) let r1 = Rectangle(10, 20) printf "%A\n" r1
Do deklarace třídy můžeme přidat i metody. Například se může jednat o metodu nazvanou Print, která vytiskne jak název objektu, tak i jeho atributy:
type Rectangle(x: int, y: int) = member this.X = x member this.Y = y member this.Print() = printf "Rectangle: %dx%d\n" this.X this.Y let r1 = Rectangle(10, 20) r1.Print()
Zajímavé ovšem je, že v metodě Print můžeme použít i hodnotu původních parametrů konstruktoru (což je v případě neměnných hodnot to stejné, jakoby se jednalo přímo o atributy objektu). Mnohdy se tedy můžeme setkat se třídami, jejichž instance sice nesou informaci o svém stavu, ale nemají deklarovány atributy:
type Rectangle(x: int, y: int) = member this.X = x member this.Y = y member this.Print() = printf "Rectangle: %dx%d\n" x y let r1 = Rectangle(10, 20) r1.Print()
13. Lokální symbol v deklaraci třídy
Již v předchozí kapitole jsme mohli vidět použití parametrů předaných konstruktoru uvnitř třídy. Programovací jazyk F# dokonce umožňuje deklaraci lokálního symbolu při konstrukci třídy; tedy hodnota navázaná na symbol bude dostupná ve vytvořeném objektu. Například si můžeme spočítat plochu obdélníka a uložit si ji do lokálního symbolu area. Hodnota je posléze použita v metodě Print:
type Rectangle(x: int, y: int) = let area = x * y member this.X = x member this.Y = y member this.Print() = printf "Rectangle: %dx%d with area %d\n" x y area let r1 = Rectangle(10, 20) r1.Print()
Samozřejmě si opět můžeme metodu Print upravit tak, aby četla hodnoty atributů:
type Rectangle(x: int, y: int) = let area = x * y member this.X = x member this.Y = y member this.Print() = printf "Rectangle: %dx%d with area %d\n" this.X this.Y area let r1 = Rectangle(10, 20) r1.Print()
Lokální symbol nelze, na rozdíl od atributů, číst vně definice třídy. To znamená, že následující program nebude přeložitelný kvůli podtrženým řádkům:
type Rectangle(x: int, y: int) = let area = x * y member this.X = x member this.Y = y member this.Print() = printf "Rectangle: %dx%d %d\n" x y area let r1 = Rectangle(10, 20) let r2 = Rectangle(1, 2) printf "%d\n" r1.X printf "%d\n" r1.Y printf "%d\n" r1.area printf "%d\n" r2.X printf "%d\n" r2.Y printf "%d\n" r2.area
14. Konstrukce nové instance třídy realizovaná v metodě
Atributy objektů jsou, pokud ovšem nebudeme explicitně žádat o jiné chování, neměnitelné (immutable), což je v mnoha ohledech výhoda. Relativně často se ovšem 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í):
type Rectangle(x: int, y: int) = member this.X = x member this.Y = y member this.Print() = printf "Rectangle: %dx%d\n" this.X this.Y member this.Enlarge(dx, dy) = Rectangle(this.X + dx, this.Y + dy)
Chování takto deklarované třídy si můžeme snadno ověřit například na tomto kódu:
let r1 = Rectangle(10, 20) r1.Print() let r2 = r1.Enlarge(1, 2) r1.Print() r2.Print()
15. Operátory definované v rámci uživatelského datového typu
V předchozím článku jsme se seznámili s tím, jakým způsobem je v programovacím jazyku F# umožněno definovat nové operátory, popř. změnit chování existujících operátorů. Ovšem prozatím jsme se věnovali jen operátorům na globální úrovni (resp. na úrovni jednotlivých modulů, o tom však až příště). V praxi je však mnohem užitečnější definice operátoru v rámci určité třídy. Například si můžeme nadefinovat třídu reprezentující dvousložkový vektor:
type Vector(x: int, y: int) = member this.X = x member this.Y = y
Pro tuto třídu, resp. přesněji řečeno pro instance této třídy, lze přetížit standardní operátor + tak, aby skutečně prováděl součet dvou vektorů a vracel vektor nový:
static member (+) (a : Vector, b : Vector) = Vector(a.X + b.X, a.Y + b.Y)
Takto vypadá třída Vector s definicí přetíženého operátoru:
type Vector(x: int, y: int) = member this.X = x member this.Y = y member this.Print() = printf "Vector: %dx%d\n" this.X this.Y static member (+) (a : Vector, b : Vector) = Vector(a.X + b.X, a.Y + b.Y) let v1 = Vector(10, 20) v1.Print() let v2 = Vector(1, 2) v2.Print() let v3 = v1 + v2 v3.Print()
Typicky u přetížených operátorů, ale i u dalších malých metod, může být výhodné použít modifikátor inline, který může vést k vygenerování rychlejšího strojového kódu. Úprava předchozího demonstračního příkladu je v tomto případě snadná:
type Vector(x: int, y: int) = member this.X = x member this.Y = y member this.Print() = printf "Vector: %dx%d\n" this.X this.Y static member inline (+) (a : Vector, b : Vector) = Vector(a.X + b.X, a.Y + b.Y) let v1 = Vector(10, 20) v1.Print() let v2 = Vector(1, 2) v2.Print() let v3 = v1 + v2 v3.Print()
16. Dědičnost
Posledním tématem, kterému se dnes budeme alespoň ve stručnosti věnovat, je realizace dědičnosti v programovacím jazyku F#. Ukažme si její využití na jednoduchém příkladu, v němž je definována nadtřída nazvaná Shape, ze které je odvozena třída Rectangle (povšimněte si řádku začínajícího klíčovým slovem inherit):
type Shape() = member this.Print() = () type Rectangle(x: int, y: int) = inherit Shape() member this.X = x member this.Y = y member this.Print() = printf "Rectangle: %dx%d\n" this.X this.Y member this.Enlarge(dx, dy) = Rectangle(this.X + dx, this.Y + dy) let r1 = Rectangle(10, 20) r1.Print() let r2 = r1.Enlarge(1, 2) r2.Print()
type Shape() = member this.Print() = printf "Shapeless shape"
Samozřejmě je možné z jedné nadtřídy odvodit více podtříd, takže se vytváří klasická hierarchie tříd:
type Shape() = member this.Print() = () type Circle(r: int) = inherit Shape() member this.R = r member this.Print() = printf "Circle: %d\n" this.R member this.Enlarge(dr) = Circle(this.R + dr) type Rectangle(x: int, y: int) = inherit Shape() member this.X = x member this.Y = y member this.Print() = printf "Rectangle: %dx%d\n" this.X this.Y member this.Enlarge(dx, dy) = Rectangle(this.X + dx, this.Y + dy) let r1 = Rectangle(10, 20) r1.Print() let r2 = r1.Enlarge(1, 2) r2.Print() let c1 = Circle(10) c1.Print() let c2 = c1.Enlarge(1) c2.Print()
17. Operátor upcast: přetypování na předka
V programovacím jazyku F# nalezneme mj. i dva „šipkové“ operátory, které jsme minule vynechali. Jeden z těchto operátorů se zapisuje znakem :> a provádí konverzi hodnoty na třídu, která se v hierarchii nachází výše. Z tohoto důvodu se tento operátor nazývá upcast.
Podívejme se nyní na základní způsob použití tohoto operátoru. Použijeme naši hierarchii tříd Shape:Circle a Shape:Rectangle:
type Shape() = member this.Print() = printf "Shapeless shape" type Circle(r: int) = inherit Shape() member this.R = r member this.Print() = printf "Circle: %d\n" this.R member this.Enlarge(dr) = Circle(this.R + dr) type Rectangle(x: int, y: int) = inherit Shape() member this.X = x member this.Y = y member this.Print() = printf "Rectangle: %dx%d\n" this.X this.Y member this.Enlarge(dx, dy) = Rectangle(this.X + dx, this.Y + dy)
Necháme si zkonstruovat několik instancí tříd Rectangle a Circle:
let r1 = Rectangle(10, 20) r1.Print() let r2 = r1.Enlarge(1, 2) r2.Print() let c1 = Circle(10) c1.Print() let c2 = c1.Enlarge(1) c2.Print()
Další objekt bude získán z instance r1, ale bude přetypován na instanci třídy Shape:
let s1 = r1 :> Shape s1.Print()
Výsledky ukazují, jak bude vše probíhat v runtime:
Rectangle: 10x20 Rectangle: 11x22 Circle: 10 Circle: 11 Shapeless shape
Existuje i operátor downcast zapisovaný znaky :?>. Jeho chování je v praxi poněkud složitější a proto se k němu ještě vrátíme v dalším článku:
let s2 = s1 :?> Rectangle s2.Print() let s3 = s1 :?> Circle s3.Print()
Tato část kódu po svém překladu a spuštění vypíše:
Rectangle: 10x20 Circle: undefined
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/f-sharp-examples/. V tabulce umístěné pod tímto odstavcem jsou uvedeny odkazy na tyto příklady:
19. Literatura
- Get Programming with F#
https://www.manning.com/books/get-programming-with-f-sharp - F# for Scientists
https://www.amazon.com/F-Scientists-Jon-Harrop-ebook/dp/B005PS97RO - Domain Modeling Made Functional
https://fsharpforfunandprofit.com/books/ - Functional Programming with F# (na Overleaf, tedy i se zdrojovými kódy)
https://www.overleaf.com/project/5bf2cb3cd9568d5a75bfcba9 - Book of F#
https://nostarch.com/fsharp - F# Programming (Wikibook)
https://en.wikibooks.org/wiki/F_Sharp_Programming - Stylish F#: Crafting Elegant Functional Code for .NET and .NET Core
https://www.amazon.com/dp/1484239997/ - ML for the Working Programmer
https://www.cl.cam.ac.uk/~lp15/MLbook/pub-details.html - Elements of ML Programming, 2nd Edition (ML97)
http://infolab.stanford.edu/~ullman/emlp.html - A tour of Standard ML
https://saityi.github.io/sml-tour/tour/welcome - The History of Standard ML
https://smlfamily.github.io/history/SML-history.pdf - The Standard ML Basis Library
https://smlfamily.github.io/Basis/ - Programming in Standard ML
http://www.cs.cmu.edu/~rwh/isml/book.pdf - Programming in Standard ML '97: A Tutorial Introduction
http://www.lfcs.inf.ed.ac.uk/reports/97/ECS-LFCS-97–364/ - Programming in Standard ML '97: An On-line Tutorial
https://homepages.inf.ed.ac.uk/stg/NOTES/ - The OCaml system release 4.13
https://ocaml.org/releases/4.13/htmlman/index.html - Real World OCaml: Functional programming for the masses
https://dev.realworldocaml.org/ - OCaml from the Very Beginning
http://ocaml-book.com/ - OCaml from the Very Beginning: More OCaml : Algorithms, Methods & Diversions
http://ocaml-book.com/more-ocaml-algorithms-methods-diversions/ - Unix system programming in OCaml
http://ocaml.github.io/ocamlunix/ - OCaml for Scientists
https://www.ffconsultancy.com/products/ocaml_for_scientists/index.html - Using, Understanding, and Unraveling The OCaml Language
https://caml.inria.fr/pub/docs/u3-ocaml/ - Developing Applications With objective Caml
https://caml.inria.fr/pub/docs/oreilly-book/index.html - Introduction to Objective Caml
http://courses.cms.caltech.edu/cs134/cs134b/book.pdf - How to Think Like a (Functional) Programmer
https://greenteapress.com/thinkocaml/index.html
20. 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 - 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 - 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 Programmers Need Limits
https://cscalfani.medium.com/why-programmers-need-limits-3d96e1a0a6db - Signatures
https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/signature-files - F# for Linux People
https://carpenoctem.dev/blog/fsharp-for-linux-people/ - Ionide project
https://ionide.io/ - FsAutoComplete
https://ionide.io/Tools/fsac.html - Interactive (.NET for Jupyter Notebook)
https://github.com/dotnet/interactive/#jupyter-and-nteract - let Bindings
https://github.com/dotnet/docs/blob/main/docs/fsharp/language-reference/functions/let-bindings.md - Lambda Expressions: The fun Keyword (F#)
https://github.com/dotnet/docs/blob/main/docs/fsharp/language-reference/functions/lambda-expressions-the-fun-keyword.md - Infographic showing code complexity vs developer experience
https://twitter.com/rossipedia/status/1580639227313676288 - OCaml for the Masses: Why the next language you learn should be functional
https://queue.acm.org/detail.cfm?id=2038036 - Try EIO
https://patricoferris.github.io/try-eio/ - Try OCaml
https://try.ocaml.pro/ - ML – funkcionální jazyk s revolučním typovým systémem
https://www.root.cz/clanky/ml-funkcionalni-jazyk-s-revolucnim-typovym-systemem/ - Funkce a typový systém programovacího jazyka ML
https://www.root.cz/clanky/funkce-a-typovy-system-programovaciho-jazyka-ml/ - Curryfikace (currying), výjimky a vlastní operátory v jazyku ML
https://www.root.cz/clanky/curryfikace-currying-vyjimky-a-vlastni-operatory-v-jazyku-ml/ - Operátor J (Wikipedia)
https://en.wikipedia.org/wiki/J_operator - Standard ML (Wikipedia)
https://en.wikipedia.org/wiki/Standard_ML - Xavier Leroy
https://en.wikipedia.org/wiki/Xavier_Leroy - Unit type
https://en.wikipedia.org/wiki/Unit_type - The Option type
https://fsharpforfunandprofit.com/posts/the-option-type/ - Operators
https://ocaml.org/docs/operators - Operator overloading
https://en.wikipedia.org/wiki/Operator_overloading