Obsah
1. Datové typy Option, Result a Array v programovacím jazyku F#
3. Definice datového typu Option
4. Vytvoření hodnoty typu Option
5. Přístup k uložené hodnotě: problematická a zcela neidiomatická varianta
6. Test, zda je hodnota naplněna či nikoli
7. Datový typ Option a pattern matching
8. Kontrola, zda jsou pokryty obě možnosti poskytované typem Option
9. Praktický příklad – hledání prvku v seznamu
10. Vylepšený výpis nalezeného či nenalezeného prvku
12. Praktický příklad – realizace funkce pro výpočet podílu dvou celočíselných hodnot
13. Vylepšený výpis podílu či chyby
15. Konstrukce pole: výčet prvků, opakování hodnoty v poli
16. Konstrukce pole s výpočtem hodnot jeho prvků
17. Modifikace (mutace) prvků pole
18. Repositář s demonstračními příklady
1. Datové typy Option, Result a Array v programovacím jazyku F#
V páté části seriálu o programovacím jazyku F# si popíšeme a na několika demonstračních příkladech ukážeme způsoby práce s dalšími v praxi mnohdy velmi užitečnými datovými typy, které lze nalézt v prakticky každém zdrojovém kódu napsaném v F#. V první řadě se jedná o datový typ nazvaný Option a taktéž o datový typ pojmenovaný Result, což jsou v podstatě monády (nelekněte se) použité, resp. přesněji řečeno převzaté mj. i do programovacího jazyka Rust. A v závěrečné části dnešního článku si navíc popíšeme způsob práce s poli (Array), které se v mnoha ohledech liší od již popsaných seznamů (List) a taktéž se pochopitelně používají v odlišných oblastech.
2. Datový typ Option
V programovacím jazyku F# se 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 typu null. Dnes si ukážeme některé možnosti, které tento datový typ programátorům nabízí. Na tomto místě je vhodné doplnit, že podobný typ ovšem nalezneme i v mnoha dalších programovacích jazycích, viz například výše zmíněný jazyk Rust apod., popř. Haskell se svým typem nazvaným Maybe.
3. Definice datového typu Option
Samotná definice datového typu Option v jazyce F# vypadá následovně:
type Option<'a> = | Some of 'a | None
Vidíme, že se ve skutečnosti vlastně jedná o výčtový typ s pouhými dvěma explicitně zapsanými (a překladači známými) hodnotami None a Some, přičemž Some „obaluje“ vlastní hodnotu typu 'a (alfa), se kterou chceme pracovat. Současně se tedy – zcela podle očekávání – jedná o generický datový typ, což v praxi znamená, že volitelná hodnota může být jakéhokoli typu (ovšem samozřejmě hlídaného překladačem již v době překladu zdrojového kódu).
Jen pro zajímavost se podívejme, jak je typ Option definovaný v Rustu:
enum Option<T> { None, Some(T), }
až na rozdílnou syntaxi se tedy jedná o sémanticky prakticky totožný datový typ.
4. Vytvoření hodnoty typu Option
V praxi je důležitější vědět, jakým způsobem se vlastně vytvoří hodnota typu Option. Víme již, že se jedná o „obalový typ“, což znamená, že hodnota typu Option je buď rovna None nebo obaluje (wrap) konkrétní hodnotu. Konstrukce proměnné typu Option může vypadat následovně:
let maybeAnswer = Some 42
Povšimněte si, že překladač si správně odvodil, že se jedná o 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"
A konečně si ukažme použití „větve“ None:
let noAnswer = None
Proměnná, do níž přiřadíme hodnotu None, bude odlišného typu, než tomu bylo u předchozích proměnných:
val noAnswer : 'a option = None
5. Přístup k uložené hodnotě: problematická a zcela neidiomatická varianta
K hodnotě, která je typem Option „obalena“, je možné přistoupit tak, že použijeme tečkovou notaci (tu jsme již viděli například u seznamů). Celý zápis by mohl vypadat následovně:
let maybeAnswer = Some 42 printf "%d" maybeAnswer.Value
Tento zápis sice může vypadat jednoduše a pro programátory lákavě, ale má několik nectností a neměl by být v praxi používán, pokud existuje alternativní varianta (a ta prakticky vždycky existuje). Problém spočívá v tom, že výpočet bude korektní jen tehdy, pokud proměnná maybeAnswer obsahuje hodnotu Some(typ). V případě, že obsahuje hodnotu None (což pochopitelně může – proč bychom jinak Option vůbec používali), může dojít k vyhození výjimky typu System.NullReferenceException.
6. Test, zda je hodnota naplněna či nikoli
S přístupem k obalované hodnotě přes proměnná.Value do značné míry souvisí i atributy (přesněji řečeno predikáty zapisované jako atributy), 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é!).
Pro hodnotu None:
let maybeAnswer = None printf "IsNone: %b" maybeAnswer.IsNone printf "IsSome: %b" maybeAnswer.IsSome
Výsledky:
IsNone: true IsSome: false
A pro libovolnou hodnotu Some(typ):
let maybeAnswer = Some 42 printf "IsNone: %b" maybeAnswer.IsNone printf "IsSome: %b" maybeAnswer.IsSome
Výsledky:
IsNone: false IsSome: true
7. Datový typ Option a pattern matching
Velká síla a elegance programovacího jazyka F# spočívá v tom, jak dokáže s datovým typem Option pracovat s využitím pattern matchingu. Ten nám totiž umožňuje velmi elegantním způsobem hodnoty typu Option(typ) zpracovávat. Podívejme se na jednoduchý příklad, v němž je implementována funkce, která zjistí, zda jí předáváme None či obalenou hodnotu. Ze zápisu je zřejmé, jak se pattern matching používá a jak vlastně obalenou hodnotu dokážeme získat:
let exists x = match x with | Some(x) -> true | None -> false let maybeAnswer1 = Some 42 let maybeAnswer2 = None printf "%b" (exists maybeAnswer1) printf "%b" (exists maybeAnswer2)
Výsledkem bude dvojice zpráv:
true false
8. Kontrola, zda jsou pokryty obě možnosti poskytované typem Option
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
Překladač v tomto případě vypíše varování, které není vhodné ignorovat:
Incomplete pattern matches on this expression. For example, the value 'None' may indicate a case not covered by the pattern(s).
Pokusme se naopak vynechat vzor Some s jeho větví:
let exists x = match x with | None -> false
Překladač opět vypíše varování, které bude tentokrát odlišné, ale opět poměrně přesně ukazuje na chybu, ke které došlo:
Incomplete pattern matches on this expression. For example, the value 'Some(_)' may indicate a case not covered by the pattern(s).
9. Praktický příklad – hledání prvku v seznamu
Podívejme se nyní na praktický příklad použití datového typu Option společně s pattern matchingem. Budeme implementovat funkci pro vyhledání prvku s nějakou hodnotou v seznamu. V případě, že prvek bude nalezen, vrátí se jeho hodnota obalená do Some(typ), pokud nalezen 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 – a asi není překvapením, že se bude v tomto konkrétním případě jednat o int. Implementace rekurzivního průchodu seznamem s vyhledáváním prvku může vypadat následovně:
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] printf "%A" (find list1 3) printf "%A" (find list2 3) printf "%A" (find list3 3) printf "%A" (find list4 3)
Výsledky:
3 null null 3
10. Vylepšený výpis nalezeného či nenalezeného prvku
Demonstrační příklad z předchozí kapitoly měl nevýhodu v tom, že se hodnota nalezeného či naopak nenalezeného prvku vypisovala přímo funkcí printf s formátovacím řetězcem „%A“ (any), což vedlo k výpisu poněkud matoucí zprávy null v případě, že prvek nalezen nebyl. Výhodnější možná bude, když se o realizaci výpisu postaráme sami. Není to nic těžkého, protože opět použijeme pattern matching:
let print_item value = match value with | Some(x) -> printf "%d" x | None -> printf "not found"
Původní demonstrační příklad tedy upravíme do následující podoby:
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 "%d" x | None -> printf "not found" let list1 = [1; 2; 3; 4] let list2 = [0; 0; 0; 0] let list3 = [] let list4 = [3] print_item (find list1 3) print_item (find list2 3) print_item (find list3 3) print_item (find list4 3)
Nyní by měly být vypsané výsledky lépe pochopitelné (resp. přesněji řečeno méně matoucí):
3 not found not found 3
11. Datový typ Result
V mnoha případech však nemusí být použití datového typu Option tím nejlepším řešením. Pro příklad nemusíme chodit daleko – 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. K tomuto účelu se v programovacím jazyku F# 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 Result<'T,'TError> = | Ok of ResultValue:'T | Error of ErrorValue:'TError
Jen pro zajímavost a doplnění se podívejme, jak je tomu v Rustu:
enum Result<T, E> { Ok(T), Err(E), }
12. Praktický příklad – realizace funkce pro výpočet podílu dvou celočíselných hodnot
Podívejme se nyní na velmi jednoduchý demonstrační příklad, na němž si ukážeme způsob použití datového typu Result. V tomto příkladu je realizována funkce pro výpočet podílu dvou celočíselných hodnot. Pokud nedojde k dělení nulou, vrátí se vypočtený výsledek „obalený“ do Ok. V opačném případě se vrátí informace o chybě zapsaná v Error.
let divide (x:int) (y:int) = match y with | 0 -> Error "divide by zero" | _ -> Ok (x/y) printf "%A\n" (divide 10 2) printf "%A\n" (divide 10 0)
Po spuštění získáme tyto dvě zprávy:
Ok 5 Error divide by zero
let divide x y = match y with | 0 -> Error "divide by zero" | _ -> Ok (x/y) printf "%A\n" (divide 10 2) printf "%A\n" (divide 10 0)
13. Vylepšený výpis podílu či chyby
Pro získání výsledku zabaleného do Ok nebo naopak informace o chybě zabalené do Error pochopitelně opět můžeme využít pattern matching, podobně jako v případě typu Option (kde nás ovšem zajímalo rozbalení hodnoty ze Some). Do předchozího demonstračního příkladu tedy například můžeme přidat funkci, která vypíše výsledek, popř. fakt, že došlo k dělení nulou:
let print_result value = match value with | Ok(x) -> printf "%d" x | Error(x) -> printf "hmm, dělení nulou"
Celý příklad bude vypadat následovně:
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(x) -> printf "%d" x | Error(x) -> printf "hmm, dělení nulou" print_result (divide 10 2) print_result (divide 1 0)
A po jeho spuštění získáme dvojici zpráv:
5 hmm, dělení nulou
14. Datový typ Array
Posledním datovým typem, s nímž se v dnešním článku seznámíme, je typ Array neboli pole. 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ů.
Pole sice mohou na první pohled vypadat podobně jako seznamy, ovšem jejich vlastnosti se v mnoha ohledech odlišují. Zásadním rozdílem je, že prvky pole je možné měnit (takže pole je jako celek měnitelné – mutable), kdežto seznamy jsou neměnitelné. Ovšem odlišné jsou i další vlastnosti, například složitosti některých operací, které je možné s oběma datovými typy provádět. Ostatně se podívejme na následující tabulku, která oba tyto homogenní datové typy porovnává z hlediska časové složitosti jednotlivých operací:
Operace | Seznam | Pole |
---|---|---|
přidání prvku | O(1) | O(n) |
přístup k prvku | O(n) | O(1) |
modifikace prvku | × | O(1) |
Z rozdílných vlastností seznamů a polí plyne i to, v jakých oblastech se tyto datové typy uplatní. Seznamy se uplatní při operacích využívajících pattern matching (kde se typicky zpracovávají prvky postupně ve stylu hlava:ocas), ale nehodí se pro náhodný přístup k prvkům. Naproti tomu pole jsou ideálním kontejnerem ve chvíli, kdy je předem známý počet prvků, vyžaduje se náhodný přístup k prvkům a/nebo jejich modifikace.
15. Konstrukce pole: výčet prvků, opakování hodnoty v poli
Pole lze v jazyku F# zkonstruovat několika možnými způsoby. Základem je 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; |] printf "%A" a
Existují ovšem i další způsoby konstrukce pole. V balíčku Array nalezneme několik konstruktorů. Jedním z nich je i konstruktor nazvaný create. Tomu se předávají dva parametry: počet prvků konstruovaného pole a hodnota těchto prvků:
let a = Array.create 10 42 printf "%A" a
Výsledkem bude:
42,42,42,42,42,42,42,42,42,42
Samozřejmě můžeme zkonstruovat i pole odlišného typu:
let a = Array.create 5 "*" printf "%A" a
let a:int[] = Array.zeroCreate 5 printf "%A" a
Pro přístup k prvkům pole se používá notace kombinující tečku+operátor indexování (toto je starší a kompatibilní způsob zápisu):
let a = [| 1; 2; 3; 4; |] printf "%d" a.[0] printf "%d" a.[1] printf "%d" a.[-1]
V novějších verzích ovšem můžete použít lidštější zápis (který je navíc od šesté verze doporučován jako ten správný):
let a = [| 1; 2; 3; 4; |] printf "%d" a[0] printf "%d" a[1] printf "%d" a[-1]
16. Konstrukce pole s výpočtem hodnot jeho prvků
Pole lze ovšem zkonstruovat i mnoha dalšími způsoby. Poměrně elegantním řešením může být použití funkce/konstruktoru Array.init, 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):
let item_x x = x+1 let a = Array.init 10 item_x printf "%A" a
Podobně ovšem můžeme vytvořit pole o deseti prvcích obsahujících sudá čísla:
let item_x x = x*2 let a = Array.init 10 item_x printf "%A" a
17. Modifikace (mutace) prvků pole
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ž známe:
let a = [| 1; 2; 3; 4; |];; printf "%A" a a.[0] <- 42;; printf "%A" a a.[1] <- 6502;; printf "%A" a
Opět platí, že v novějších variantách jazyka F# lze psát:
let a = [| 1; 2; 3; 4; |];; printf "%A" a a[0] <- 42;; printf "%A" a a[1] <- 6502;; printf "%A" a
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 - 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 - 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/ - 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/