Už klasicky bezpečný divide v Leanu (bez monád) :)
def divide (x : Nat) (y : { n : Nat // n ≠ 0 }) : Nat := x / y
def main : IO Unit := do
IO.println s!"{divide 4 ⟨2, by decide⟩}"
Ta věc v Rustu je něco jiného, neumí zajistit nenulovost během překladu (staticky). V tom Leanu a spol. jde mít libovolnou podmínku, například sudosti nebo prvočíselnosti, která se ověří už během překladu. Za běhu to umí každý jazyk.
takze nezajisti exaktni typ parametru b? podle zapisu to tak vypada, ze to proste nepovoli (ale to je fakt uz asi hodne hifi)
u Rustu. Jaky ma ten jazyk moznosti jak reagovat na nulovej parametr "b" v runtime, kdyz se vraci usize?
Právě proto je lepší vracet Option, když jazyk nemá rovnostní typy. Ale je to za běhu režie navíc, tomu se Lean vyhne.
Oprav mě, pokud se pletu.
Pokud je možné nějaký (validační) problém vyřešit pomocí Option, je to lepší jak závislostní/rovnostní/anything typy, protože je to rychlejší.
Aktuálně se domnívám, že na problémy typu dělení nulou jsou nejlepší způsob Checked exceptions. Protože jsou ideálním kompromisem výkonu a čitelnosti. (Ještě nejlíp, když by to bylo ve skutečnosti jen zamaskované Option :-))
Postrádám nějaké pěkné demonstrace ne v čem Lean řeší problémy jinak, ale s čím si Lean poradí, zatímco jiné jazyky to nedokážou.
Abychom si rozuměli, třeba právě dělení nulou tento případ není. Obvykle totiž nemáš problém v tom, že ti funkce calculate(15, 65) zbuchne na dělení nulou, ale že ti zbuchne calculate(uservstup1, uservstup2). Takže potřebuješ kontrolu zda počítáš s neočekávaným, což už mainstreamové jazyky umějí.
Option je pomalejší, protože přidává režii za běhu. Typy se řeší jen při překladu (takže teoreticky můžou zpomalit překlad, to ostatně dělají i implicitní koerce, proto je vyhodili ze Swiftu). Příklad s dělením není moc zajímavý, protože je triviální, ale je převzatý z článku.
V praxi se používá kombinace. Option/Result je de facto standard (v Haskellu se to tak píše dekády a rozšířilo se to i jinam, protože se to osvědčilo) a pochopí to každý, to je výhoda. Sofistikované typy dává smysl používat někde hluboko v knihovnách, kde jsou schované, dělají svou užitečnou práci a nematou běžného Pepu :)
Jinak samozřejmě platí to o "exhaustive unit tests", ale to taky chce podporu standardní knihovny jazyka a vysokou úroveň abstraktního myšlení.
Rozumím. Jako čistě teoreticky je asi možné, že ten Option bude něco málo žrát, a ano uvědomuji si, že Typy řeší compiletime, ale má poznámka směřoval k tomu, že:
1/ těch míst, kde by typy tu aserci vyhodili jako nepotřebnou a kompilátor s Option ne bude malinko. V drtivé většině případů ty Typy udělají úplně to samé a vynutí logiku do runtime stejně jako Option.
2/ že Option je hnusnej.
Jestli “asercí” myslíš tu podmínku, tak tu překladač odstraní vždy, ta je jen pro typovou kontrolu a za běhu reprezentaci nemá (je to i popsané v dokumentaci).
Obecně jde asi spíš o signaturu, funkce bez Option je hezčí a obecně je rozumnější, když validaci dělá volající. Nic víc bych v tom nehledal.
podle docky je tam par unchecked operaci, ktere mohou pretect. Urcite to nedokaze rozhodnout prekladac.
Samozřejmě to zajistí nenulovost v době překladu. Dělitel je třeba připravit pomocí NonZeroUsize::new(x), což vrací Option<NonZeroUnsize> a nevhodnou hodnotu to prostě odmítne. To znamená, že se ta invalidní hodnota vyřeší někde na vstupu, což je stejně obecně nutno dělat v každém jazyce, pokud se vstup čte zvenku.
Hej, keď som tam dal nulu, tak mi to linter podčiarkol červenou. Tie taktiky sa používajú aj pri klasických programoch, nie len pri teorémach?
Ano, taktiky jsou pro všeobecné použití. Člověk si ten důkaz může vypsat, když se to učí, ale jakmile jeden pochopí rfl, nomatch a noConfusion, tak je lepší šetřit čas (a množství kódu).
“kdyz je typovej system takto silnej, je to pekne”
Bych nejásal ;)
https://doc.rust-lang.org/stable/std/num/struct.NonZeroUsize.html#method.new_unchecked
Tento typ je brutální antipattern.
Rozdíl je, že Rust podobné podmínky může ověřovat jen za běhu. V Leanu apod. jde dát do typu logický výraz, jehož platnost ověřuje překladač při překladu, do výkonného kódu se ty podmínky vůbec nedostanou.
Jestliže se načítá - někde - hodnota z reálného světa, runtime to prostě řešit musí. Umělé příklady, kde se konstanty zadávají přímo v kódu, sice možné jsou, ale nechávají mne chladným. Předpokládám nicméně, že by tohle nějaké makro zvládlo zařídit taky.
Ale jo prakticky to urcite tak nevadi. Urcite solidnejsi a lepsi reseni nez v jakemkoliv mainstream jazyku. Ale to pak to deleni nulou lze osetrit podobne v kazdem jayzce jako v tom Rustu s trochou práce.
Ja teda produkcni software s dependent types tedy nevidel, to je fakt, ale lze s nima modelovat fakt precizne.
12. 10. 2023, 17:38 editováno autorem komentáře
Ja teda produkcni software s dependent types tedy nevidel, to je fakt, ale lze s nima modelovat fakt precizne.
Ano, dokonce tak, že není třeba psát ani ty programy a stačí psát pouze typy.
Třeba do OCamlu extrahuje verifikované programy Coq. A tam už pár netriviálních programů existuje: https://github.com/coq-community/awesome-coq#verified-software Blbé je, že OCaml není verifikovaný, takže tam se to může rozbít.
Problém je, že by člověk potřeboval verifikovaný kompilátor, os i hw.
My ale nepotrebujeme hneď nejaký dokolaný jazyk. Stačí aby sa táto pokročilá práca s typmi postupne dostala do mainstreamu, tak ako funkcionálne programovanie a kvalita progamov sa zvýši.
Unit tests with exhaustive coverage -- another name for mathematical theorems.
https://www.youtube.com/watch?v=8WFMK0hv8bE
Pekne to vysvetľuje v tomto videu jeden matematik na príklade Agdy. Pokročílá práca s typmi môže trebárs v budúcnosti nahradiť unit testy.
To tu už kdysi psal BoneFlute :) Teoreticky to je pravda, ovšem kolik lidí takový typový systém pochopí?
Ošetřit jde, ale otázkou je: Jak to bude hezké, jak to bude bezpečné a jak to bude rychlé.Vždycky znovu mě u Rustu překvapí, jak na praktické věci má poměrně hezké řešení. A co ještě neumí, na tom někdo pravděpodobně pracuje.
12. 10. 2023, 20:27 editováno autorem komentáře
„lze s nima modelovat fakt precizne”
No jo, je to skoro logika 1. řádu. Problémem je možná absence rozsáhlejších knihoven. Z poměrně běžných jazyků je má jen Scala (ale tam je slabší odvar).
Pak překladač musí dokázat, že dělitel je nenulový. Což může být pro překladač o občas i pro člověka celkem netriviální.
Nebo to má třeba jako Isabelle možnost napojení automatických dokazovačů?
Super, ten bind pekne odpovida na to, co jsme resili u F#. Tim ze se z toho udelal "operator" to vypada hodne jako threading makro nebo znak pajplajny. Zacinam mit ML jazyky rad :)
my jsme se kdysi učili, že je to kolona (viz článek). To je možná lepší název, protože dneska je "pajplajna" v IT strašně nadužívanej termín. Až tak naduživanej, že to kolegové používají pro označení své technlogie s batch processingem (prostě jednou za den jim cron něco spustí a říkají tomu pajplajna).
Kromě vracení option nebo result je v OCamlu i celkem běžné vyhazovat výjimku.
Třeba funkce List.find ze standardní knihovny vyhodí Not_found, když nenajde prvek, který splňuje predikát.
Výhodou je, že kód vyhazující výjimky nemusí při úspěchu obalit každý výsledek do další hodnoty, takže míň zatěžuje GC. A vyhazování nebo chycení výjimky v OCamlu je rychlejší než v C++ nebo pod JVM či CLR.
ja o tom "railroad programmingu" uz videl par videi a je fakt, ze zrovna OCaml tam neukazovali. Ale byl to F# a snad Rust (ale to presne nevim)
Tak asi F# for Fun and Profit. Jinak to je obecne technika ktera se vaze k monadickemu retezeni, ktere se da vlastne implementovat celkem dobre i v jinejch jazycich. Jen ty funkcionalni z rodiny ML to maj vetsinou out of the box.
jj já o tom poprvé četl také tam (F# for F&F): https://fsharpforfunandprofit.com/posts/recipe-part2/