Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: Kit 14. 05. 2016, 10:46:22
-
Ve vlákně o Pythonu se rozhořela vášnivá debata o Haskellu. S tímto jazykem teprve začínám a zatím narážím na obtížnou čitelnost některých zdrojáků. Vím, že si na to časem zvyknu, ale přesto se ptám: Má Haskell budoucnost?
-
co máte na mysli, když píšete "budoucnost"? masové rozšíření? dlouhodobé přežívání?
-
IMO jednou z hlavních nevýhod Haskellu je, že vyhodnocování není standardně striktní. Tohle například velmi ztěžuje uvažování o tom, kolik paměti program využívá (nestačí uvažovat o funkcích jednotlivě, je třeba uvažovat o celém programu naráz - využitá paměť nezávisí jen na tom, s jakými argumenty je funkce volána, ale i na tom, jaké části výsledku (ne)jsou vyhodnoceny a kdy).
Další nevýhodou GHC Haskellu je komplikovaný typový systém - je to dáno velkým množstvím různých rozšíření, jejichž funkčnost se všelijak překrývá. Navíc vyjádřit v něm zajímavější vlastnosti je komplikované. Oproti jazykům se závislými typy má lepší typovou inferenci, ale IMO ta za to nestojí.
Dále mi v Haskellu chybí silnější systém záznamů (jako má třeba Ur/Web) a podtypový polymorfismus.
A nakonec se mi nelíbí některé rozšířené styly programování v Haskellu. Zejména používání monadických transformátorů - raději bych měl podporu efektů přímo v typovém systému nebo alespoň používal něco jako Eff z Idrisu - oba berou v úvahu i komutující efekty.
-
A ještě jedna věc: nelíbí se mi, že typová třída nemůže mít pro jeden typ více instancí.
-
ta jako chcete říct, že ghc(/haskell) není dokonalý, jo?
-
Jedině Java, to je královský jazyk. Všechno kolem (třeba Python) je fajn tak na to domácí hraní, nebo hodně speciální případy.
-
co máte na mysli, když píšete "budoucnost"? masové rozšíření? dlouhodobé přežívání?
Nejspíš přežívání v akademické sféře.
-
Hlavně mi připadalo zajímavé, co se ve vlákně o Pythonu psalo o Haskellu. Bohužel tedy v místě, kde by to nikdo nehledal, snad jen Google.
Mně se na použití Haskellu nelíbí hlavně ty příšerně dlouhé názvy funkcí, které mnozí programátoři používají. Proč, když máme propracovaný systém namespaces?
-
Ma Haskell pritomnost? Ak si OK s aktualnym stavom rozsirenia a vyuzitim, potom ma zmysel riesit, ci sa to udrzi.
-
co máte na mysli, když píšete "budoucnost"? masové rozšíření? dlouhodobé přežívání?
Nejspíš přežívání v akademické sféře.
Některé vlastnosti Haskellu jsou mi sympatické, jiné méně a některé mě dokonce i prudí. To se však může změnit, pokud se na to budu dívat trochu z jiného úhlu a naučím se číst symboly "=>", "->", "|>" apod. Je to pro mne prostě nezvyklý zápis a čumím na to jak na nejnovější C++.
-
Haskell má svoje mouchy - recordy nejsou úplně dobré, systém modulů taky ne. Pro přístup k recordům se dají použít Lens, spíš je to tak, že se Lensy nechce člověku generovat pořád a všude, takže je z toho pak trošku nešikovný mix.
Monadické transformery jsou taky něco, co je trochu zvláštní - teď zrovna koukám do PureScriptu s extensible effects - ale zatím nemám názor na to, zda je to nějak výrazně lepší nebo horší. Určitě to umožňuje to "granulárnější" kontrolu efektů - v Haskellu je to v zásadě buď IO nebo Free, extensible effects umožňují vlastně tagovat funkce, jaké efekty mají - což vlastně není vůbec špatné.
IMO jednou z hlavních nevýhod Haskellu je, že vyhodnocování není standardně striktní. Tohle například velmi ztěžuje uvažování o tom, kolik paměti program využívá (nestačí uvažovat o funkcích jednotlivě, je třeba uvažovat o celém programu naráz - využitá paměť nezávisí jen na tom, s jakými argumenty je funkce volána, ale i na tom, jaké části výsledku (ne)jsou vyhodnoceny a kdy).
Tohle je častý názor, a vůbec ho nechápu. Stačí se podívat třeba na to, jak to má purescript, který je naopak strict: https://pursuit.purescript.org/packages/purescript-maybe/0.3.5/docs/Data.Maybe (https://pursuit.purescript.org/packages/purescript-maybe/0.3.5/docs/Data.Maybe). To má být jako lepší? Ano, problematika space-leaku v haskellu je, ale po prvotních "úspěších" se mi to ve standardním kódu stává extrémně zřídka (na druhou stranu, když se mi to stane, tak to stojí za to a hraničí to s chybou překladače).
A ještě jedna věc: nelíbí se mi, že typová třída nemůže mít pro jeden typ více instancí.
Když jsem s haskellem začínal, tak jsem si to myslel taky. Ale za posledních několik projektů si neuvědomuju, že bych vůbec uvažoval o tom něco takhle použít. Máte nějaký praktický příklad, kde to vadí?
Připadá mi, že je to v současné době snad jediný jazyk, který má zároveň velmi silný typový systém a používá se poměrně hodně v praxi - tzn. nikoliv specializované projekty, ale naprosto normální "business". Ano, existují jazyky se silnějším typovým systémem, ale v porovnání s jazyky typu Java, C# jsme někde úplně jinde... navíc v haskellu se snaží pomalinku dependent types implementovat, takže budoucnost je otevřená :)
Některé vlastnosti Haskellu jsou mi sympatické, jiné méně a některé mě dokonce i prudí. To se však může změnit, pokud se na to budu dívat trochu z jiného úhlu a naučím se číst symboly "=>", "->", "|>" apod. Je to pro mne prostě nezvyklý zápis a čumím na to jak na nejnovější C++.
No jo, tak chvíli trvá, než to člověk pochopí....třeba u tohodle je nejdůležitější pochopit, jaké je "správné uzávorkování", pak je to najednou triviální :)
<code>f <$> a <*> b <*> c</code>
-
Mně se na použití Haskellu nelíbí hlavně ty příšerně dlouhé názvy funkcí, které mnozí programátoři používají. Proč, když máme propracovaný systém namespaces?
??? Tomu nerozumím, to je problém Javy, ne Haskellu :) Kde jsi narazil na dlouhá jména funkcí?
-
A ještě jedna věc: nelíbí se mi, že typová třída nemůže mít pro jeden typ více instancí.
Když jsem s haskellem začínal, tak jsem si to myslel taky. Ale za posledních několik projektů si neuvědomuju, že bych vůbec uvažoval o tom něco takhle použít. Máte nějaký praktický příklad, kde to vadí?
mám pomocí Free monády vytvořený AST a chci dva interpretry, jeden co generuje cčkový kód a druhý co přímo vykonává, existuje na to trik s newtype, ale ještě se mi to nepovedlo realizovat
-
Mně se na použití Haskellu nelíbí hlavně ty příšerně dlouhé názvy funkcí, které mnozí programátoři používají. Proč, když máme propracovaný systém namespaces?
??? Tomu nerozumím, to je problém Javy, ne Haskellu :) Kde jsi narazil na dlouhá jména funkcí?
Dlouhá jména funkcí nemusí být nevýhodou, v kombinaci s vhodně zvolenou konvencí nahrazují dokumentaci.
-
A ještě jedna věc: nelíbí se mi, že typová třída nemůže mít pro jeden typ více instancí.
Když jsem s haskellem začínal, tak jsem si to myslel taky. Ale za posledních několik projektů si neuvědomuju, že bych vůbec uvažoval o tom něco takhle použít. Máte nějaký praktický příklad, kde to vadí?
mám pomocí Free monády vytvořený AST a chci dva interpretry, jeden co generuje cčkový kód a druhý co přímo vykonává, existuje na to trik s newtype, ale ještě se mi to nepovedlo realizovat
A proč máte interpret jako typeclass?
-
Mně se na použití Haskellu nelíbí hlavně ty příšerně dlouhé názvy funkcí, které mnozí programátoři používají. Proč, když máme propracovaný systém namespaces?
??? Tomu nerozumím, to je problém Javy, ne Haskellu :) Kde jsi narazil na dlouhá jména funkcí?
Například zde: https://github.com/Ohlasy/web/blob/gh-pages/_bin/top-articles.hs (https://github.com/Ohlasy/web/blob/gh-pages/_bin/top-articles.hs). Ty názvy funkcí mi připadají jako utřiNosoČistoPlenou nebo zahrajNaKlapkoBřinkoStroj. Je však docela možné, že jsem prostě náhodou narazil na nevhodný zdroják.
-
Mně se na použití Haskellu nelíbí hlavně ty příšerně dlouhé názvy funkcí, které mnozí programátoři používají. Proč, když máme propracovaný systém namespaces?
??? Tomu nerozumím, to je problém Javy, ne Haskellu :) Kde jsi narazil na dlouhá jména funkcí?
Dlouhá jména funkcí nemusí být nevýhodou, v kombinaci s vhodně zvolenou konvencí nahrazují dokumentaci.
Dlouhá jména funkcí se blbě čtou, dokumentaci nenahradí a často se taková funkce ani nevejde na jeden řádek a musí se zalamovat.
-
A proč máte interpret jako typeclass?
dobrá otázka, psaní toho kódu už na mě bylo intelektuálně trochu moc (jsem přece jenom prostý programátor C/C++), takže moje odpověď je poněkud mlhavá - má to něco společného se skládáním více "jazyků", motivovaným korektností použití příkazů pro ukončení iterace (tj. break a continue může být jenom v cyklu, jinak to ghc nepustí)
-
Tohle je častý názor, a vůbec ho nechápu. Stačí se podívat třeba na to, jak to má purescript, který je naopak strict: https://pursuit.purescript.org/packages/purescript-maybe/0.3.5/docs/Data.Maybe (https://pursuit.purescript.org/packages/purescript-maybe/0.3.5/docs/Data.Maybe). To má být jako lepší?
Tomuhle moc nerozumím - co jsi tím chtěl říct? Jak souvisí Maybe s lazy/strict eval? A jak se liší tenhle Maybe od toho haskellovského?
-
Tohle je častý názor, a vůbec ho nechápu. Stačí se podívat třeba na to, jak to má purescript, který je naopak strict: https://pursuit.purescript.org/packages/purescript-maybe/0.3.5/docs/Data.Maybe (https://pursuit.purescript.org/packages/purescript-maybe/0.3.5/docs/Data.Maybe). To má být jako lepší?
Přijde mi lepší explicitně říci, že se něco konkrétního nemá vyhodnocovat striktně, než naopak - osobně chci častěji vyhodnocovat hned než nevyhodnocovat. Pokud jde o to, že při každém volání fromMaybe' musím argument obalit funkcí, tak to jde vyřešit tím, že to udělá kompilátor na základě typu (například když typ parametru bude Lazy Int, tak to kompilátor zabalí automaticky - tj. argument se nebude vyhodnocovat).
Ano, problematika space-leaku v haskellu je, ale po prvotních "úspěších" se mi to ve standardním kódu stává extrémně zřídka (na druhou stranu, když se mi to stane, tak to stojí za to a hraničí to s chybou překladače).
Osobně mi přijde, že je takové chyby velmi těžké odhalit viz třeba Sometimes, the old ways are the best (http://www.serpentine.com/blog/2015/05/13/sometimes-the-old-ways-are-the-best/). Na druhou stranu v Haskellu už dlouhou dobu neprogramuji.
Když jsem s haskellem začínal, tak jsem si to myslel taky. Ale za posledních několik projektů si neuvědomuju, že bych vůbec uvažoval o tom něco takhle použít. Máte nějaký praktický příklad, kde to vadí?
Třeba u typové třídy Ord by mi to přišlo docela užitečné. Nebo třeba u monoidu se to musí obcházet pomocí newtype Sum a newtype Product. A nakonec se může stát, že dvě různé knihovny obsahují různé instance pro jeden typ, a pokud se nepletu, tak GHC program neslinkuje?
Připadá mi, že je to v současné době snad jediný jazyk, který má zároveň velmi silný typový systém a používá se poměrně hodně v praxi - tzn. nikoliv specializované projekty, ale naprosto normální "business".
Takových jazyků příliš není, to je pravda. Dalo by se říci, že do téhle oblasti zasahuje ještě OCaml a Scala (resp. Dotty).
-
Mně se na použití Haskellu nelíbí hlavně ty příšerně dlouhé názvy funkcí, které mnozí programátoři používají. Proč, když máme propracovaný systém namespaces?
??? Tomu nerozumím, to je problém Javy, ne Haskellu :) Kde jsi narazil na dlouhá jména funkcí?
Dlouhá jména funkcí nemusí být nevýhodou, v kombinaci s vhodně zvolenou konvencí nahrazují dokumentaci.
Přesně tak, Java ví, jak se správně píše.
-
Citace z vlákna o pythonu, ať to tam nespamujem ;)
Na cvikach se to do nas snazili nejak nalit, kdyz zjistili, ze to nikdo nechape, ale bylo malo casu a musely se resit predepsane prakticke priklady. Ty prekvapive s vyucovanou teorii nemeli nic moc spolecneho (asi jsme to v tom jen nevideli, nevim, protoze snad ta teorie nebyla nanic?). Treba ty monady a do notaci jsem pochopil az mnohem pozdeji samostudiem pri praci na projektu. Jak jsem byl nastvany na prednasejiciho, ze takovou "nepodstatnou" vec mi zamlcel.
Mě začalo docela bavit koukat se, jakým způsobem různí lidi Haskell (a speciálně monády) vysvětlují. Přijde mi to jako hrozně zajímavé téma, protože to fakt není vůbec lehký vysvětlit dobře a tímpádem se na tom výborně ukazují pedagogické schopnosti. Skoro by se to hodilo jako test na vstupní pohovor pro vyučující (kdyby se něco takového dělalo, většinou nedělá).
Přijde mi totiž, že zrovna u monád se vůbec nevyplatí se odpíchnout od vysvětlování formalismu. V tom se drtivá většina lidí zasekne a dál se nedostane, takže v nich nezůstane vůbec nic, což je ta nejhorší možná varianta. Ono tam jde totiž vlastně o to, že úplně jednoduchý princip (skládání fcí) je z nějakých důvodů potřeba řešit poměrně komplikovaně a vychází to z ještě komplikovanějšího formálního aparátu. Takže jsem po dlouhých úvahách došel k tomu, že je potřeba člověku prvně nějak neformálně ukázat, proč to je a k čemu to vlastně slouží - a teprve potom, až má nějakou mlhavou představu, o čem je vlastně řeč, ukázat prvně buď příklad, nebo velice zlehounka ten formalismus.
Mně třeba největší aha moment přinesla nějaká prezentace, kde týpek vysvětloval monády jako větvení vlakových kolejí - když nedojde k chybě, jede vlak dál touhle cestou, a když dojde, přejede přes tuhle vyhýbku... Bohužel to teď asi nedohledám, ale tohle byla fakt první věc, na které jsem vůbec chytl, o co vlastně teda jde.
...a když tohle člověk chytne, tak už potom dá ty příklady - ale imho by jako první měl vidět příklad rozepsaný pomocí lambd, BEZ do notace a těch speciálních operátorů. Pokud to totiž uvidí napsané v do notaci, získá falešný dojem, že tomu rozumí ("jasně, to je jako v imperativních jazycích") a je úplně mimo, protože nepochopil vůbec nic.
Prostě, mám pocit, že tohle téma může dobře vysvětlit jenom člověk, kterej nemá potřebu dělat chytrýho, ale naopak se rád sníží k "dětinským" příkladům, aby studenty látku efektivně naučil. A takových učitelů je bohužel na našich VŠ asi dost málo... Je to škoda. Zrovna tohle by si zasloužil znát každý mírně nadprůměrný programátor...
Jako chapu, ze ciste FP neni moc popularni. Ale vzhledem k tomu, jak se mu uspesne dari infiltrovat popularni jazyky, tak si myslim, ze minimalne jeden predmet v bakalari by si to zaslouzilo.
Určitě! Hele, upřímně, na Haskell jako takovej sere pes. Je to super obohacení, ale když ho nebudeš znát, nic moc se nestane. Co by ale každej měl znát (na škole minimálně "ochutnat"), je ten FP způsob myšlení - důraz na základní datové struktury a operace nad nimi, proudové zpracování, řetězení, atd. atd.
Ono totiž FP je úžasný způsob, jak si prakticky osahat ty datové struktury, o kterých se člověk učí v teorii...
Rozhodne nerazim nazor "Vsude ciste FP!". Treba prave ta Scala neni ani omylem cista (Haskellisti se na ni myslim celkem casto divaji shora) a FP se na nektere veci hodi mnohem vice, na jine naopak vice vynika imperativni pristup a OOP.
Myslím, že je úžasný koukat na to, jak Haskell razí principielní přístup bez výjimek a způsobuje si tím zpoustu problémů, které se nedají řešit jinak než relativně komplikovaně. A vedle něj jsou pragmatičtější jazyky, které jdou trochu "špinavější" cestou, ztrácí tím některé prima vlastnosti Haskellu, ale zase nemají ty jeho problémy a tímpádem se dají lehčeji naučit a líp se kombinují s tím, co člověk zná odjinud.
FP určitě do mainstreamu pronikat bude (už se to masově děje), ale řekl bych, že spíš v takové té pragmatičtější formě - jako volitelná součást, použitelná, pokud programátor chce, ale nesvazující ho tam, kde nechce.
-
Mně se na použití Haskellu nelíbí hlavně ty příšerně dlouhé názvy funkcí, které mnozí programátoři používají. Proč, když máme propracovaný systém namespaces?
??? Tomu nerozumím, to je problém Javy, ne Haskellu :) Kde jsi narazil na dlouhá jména funkcí?
Například zde: https://github.com/Ohlasy/web/blob/gh-pages/_bin/top-articles.hs (https://github.com/Ohlasy/web/blob/gh-pages/_bin/top-articles.hs). Ty názvy funkcí mi připadají jako utřiNosoČistoPlenou nebo zahrajNaKlapkoBřinkoStroj. Je však docela možné, že jsem prostě náhodou narazil na nevhodný zdroják.
OMG, to by byl hnus i v Javě :D Takhle je to trochu moc, no.
-
A proč máte interpret jako typeclass?
dobrá otázka, psaní toho kódu už na mě bylo intelektuálně trochu moc (jsem přece jenom prostý programátor C/C++), takže moje odpověď je poněkud mlhavá - má to něco společného se skládáním více "jazyků", motivovaným korektností použití příkazů pro ukončení iterace (tj. break a continue může být jenom v cyklu, jinak to ghc nepustí)
Mno... já teda mám zkušenost spíš s Operational, ale pořád to tak nějak nechápu... interpret je přece normální funkce, která má vstupem "Free a" (tzn. "data") a výstupem je cokoliv. Nějak nerozumím, proč by se nad čímkoliv (včetně té "instrukce") měla dělat jakákoliv typeclas...ale to už je asi otázka toho konkrétního kódu :)
-
Mně se na použití Haskellu nelíbí hlavně ty příšerně dlouhé názvy funkcí, které mnozí programátoři používají. Proč, když máme propracovaný systém namespaces?
??? Tomu nerozumím, to je problém Javy, ne Haskellu :) Kde jsi narazil na dlouhá jména funkcí?
Například zde: https://github.com/Ohlasy/web/blob/gh-pages/_bin/top-articles.hs (https://github.com/Ohlasy/web/blob/gh-pages/_bin/top-articles.hs). Ty názvy funkcí mi připadají jako utřiNosoČistoPlenou nebo zahrajNaKlapkoBřinkoStroj. Je však docela možné, že jsem prostě náhodou narazil na nevhodný zdroják.
OMG, to by byl hnus i v Javě :D Takhle je to trochu moc, no.
I v Javě jsou dlouhé názvy zbytečné. Vždyť má namespaces.
-
Mně třeba největší aha moment přinesla nějaká prezentace, kde týpek vysvětloval monády jako větvení vlakových kolejí -
Ten samý princip je použitý tady: http://www.zohaib.me/yet-another-what-is-a-monad-post/ Ale není to ten článek, co mi tenkrát přinesl ten aha moment :)
-
Mně se na použití Haskellu nelíbí hlavně ty příšerně dlouhé názvy funkcí, které mnozí programátoři používají. Proč, když máme propracovaný systém namespaces?
??? Tomu nerozumím, to je problém Javy, ne Haskellu :) Kde jsi narazil na dlouhá jména funkcí?
Například zde: https://github.com/Ohlasy/web/blob/gh-pages/_bin/top-articles.hs (https://github.com/Ohlasy/web/blob/gh-pages/_bin/top-articles.hs). Ty názvy funkcí mi připadají jako utřiNosoČistoPlenou nebo zahrajNaKlapkoBřinkoStroj. Je však docela možné, že jsem prostě náhodou narazil na nevhodný zdroják.
OMG, to by byl hnus i v Javě :D Takhle je to trochu moc, no.
I v Javě jsou dlouhé názvy zbytečné. Vždyť má namespaces.
Proto se tam nedělají. Máš tam normální názvy a nepotřebuješ tak často psát dokumentaci.
-
A proč máte interpret jako typeclass?
dobrá otázka, psaní toho kódu už na mě bylo intelektuálně trochu moc (jsem přece jenom prostý programátor C/C++), takže moje odpověď je poněkud mlhavá - má to něco společného se skládáním více "jazyků", motivovaným korektností použití příkazů pro ukončení iterace (tj. break a continue může být jenom v cyklu, jinak to ghc nepustí)
Mno... já teda mám zkušenost spíš s Operational, ale pořád to tak nějak nechápu... interpret je přece normální funkce, která má vstupem "Free a" (tzn. "data") a výstupem je cokoliv. Nějak nerozumím, proč by se nad čímkoliv (včetně té "instrukce") měla dělat jakákoliv typeclas...ale to už je asi otázka toho konkrétního kódu :)
problém nastal, když jsem začal skládat dva jazyky, jeden "normální", druhý (tvořený příkazy break a continue) povolený jen v těle iterace jako součet s oním "normálním", pak je nutné použít stejnou interpretační funkci na dva různý typy
jestli to zní zmateně, tak to může být i tím, že já sám jsem
-
Tomuhle moc nerozumím - co jsi tím chtěl říct? Jak souvisí Maybe s lazy/strict eval? A jak se liší tenhle Maybe od toho haskellovského?
Kouknul jsi se na ten link? Kvůli maybe potřebuješ dvojnásobek funkcí, protože jak je to strikt, tak musíš řešit, že by se třeba ten druhý parametr musel vyhodnocovat dlouho. Takže oproti zcela triviální implimentaci Maybe v haskellu to je docela prohra.
Přijde mi lepší explicitně říci, že se něco konkrétního nemá vyhodnocovat striktně, než naopak - osobně chci častěji vyhodnocovat hned než nevyhodnocovat. Pokud jde o to, že při každém volání fromMaybe' musím argument obalit funkcí, tak to jde vyřešit tím, že to udělá kompilátor na základě typu (například když typ parametru bude Lazy Int, tak to kompilátor zabalí automaticky - tj. argument se nebude vyhodnocovat).
No mě to taky připadá lepší, když je něco explicitně - takže automatické obalování v žádném případě ne.
Ale jinak: Pro ty, kteří opravdu bazírují na výkonnosti tu máme https://ghc.haskell.org/trac/ghc/wiki/StrictPragma (https://ghc.haskell.org/trac/ghc/wiki/StrictPragma). Stačí to dát na začátek modulu (nebo do Cabalu) a jede se... ale osobně kromě snad nějakého výpočetního důvodu to fakt nemám důvod použít.
Třeba u typové třídy Ord by mi to přišlo docela užitečné. Nebo třeba u monoidu se to musí obcházet pomocí newtype Sum a newtype Product. A nakonec se může stát, že dvě různé knihovny obsahují různé instance pro jeden typ, a pokud se nepletu, tak GHC program neslinkuje?
Tak to je dobře (GoodThing), že to neslinkuje :) Proto taky GHC varuje před OrphanInstances a když jsou v knihovně OrphanInstances tak to je BadThing. On na první pohled ten newtype vypadá "kostrbatě", ale mě třeba připadá, že jinak to stejně nejde - nebylo by to "composable".
Třeba jsem řešil, že chci ve FromJSON struktuře deserializovat něco jinak, než je v default typeclass - třeba některá čísla tam jsou hexadecimálně ve stringu. A nevím, jak by se tohle řešilo - přiřadit typeclass konkrétní položce v recordu? A pak ta hodnota bude i s tou typeclass někde cestovat?
Myslím, že je úžasný koukat na to, jak Haskell razí principielní přístup bez výjimek a způsobuje si tím zpoustu problémů, které se nedají řešit jinak než relativně komplikovaně.
Nevím, mě to přijde tak trochu ve stylu "počítače pomáhají řešit problémy, které by bez nich neexistovaly". Spousta problémů, které se v Haskellu řeší, v jiných jazycích neexistují, protože se tam vůbec něco takového nedá vyjádřit.
-
problém nastal, když jsem začal skládat dva jazyky, jeden "normální", druhý (tvořený příkazy break a continue) povolený jen v těle iterace jako součet s oním "normálním", pak je nutné použít stejnou interpretační funkci na dva různý typy
jestli to zní zmateně, tak to může být i tím, že já sám jsem
Aniž bych viděl kód - někdo tu linkoval http://www.cs.ru.nl/~W.Swierstra/Publications/DataTypesALaCarte.pdf (http://www.cs.ru.nl/~W.Swierstra/Publications/DataTypesALaCarte.pdf) - vypadá to jako dost podobné tomu, co děláš :)
-
Citace z vlákna o pythonu, ať to tam nespamujem ;)
Na cvikach se to do nas snazili nejak nalit, kdyz zjistili, ze to nikdo nechape, ale bylo malo casu a musely se resit predepsane prakticke priklady. Ty prekvapive s vyucovanou teorii nemeli nic moc spolecneho (asi jsme to v tom jen nevideli, nevim, protoze snad ta teorie nebyla nanic?). Treba ty monady a do notaci jsem pochopil az mnohem pozdeji samostudiem pri praci na projektu. Jak jsem byl nastvany na prednasejiciho, ze takovou "nepodstatnou" vec mi zamlcel.
Mě začalo docela bavit koukat se, jakým způsobem různí lidi Haskell (a speciálně monády) vysvětlují. Přijde mi to jako hrozně zajímavé téma, protože to fakt není vůbec lehký vysvětlit dobře a tímpádem se na tom výborně ukazují pedagogické schopnosti. Skoro by se to hodilo jako test na vstupní pohovor pro vyučující (kdyby se něco takového dělalo, většinou nedělá).
Přijde mi totiž, že zrovna u monád se vůbec nevyplatí se odpíchnout od vysvětlování formalismu. V tom se drtivá většina lidí zasekne a dál se nedostane, takže v nich nezůstane vůbec nic, což je ta nejhorší možná varianta. Ono tam jde totiž vlastně o to, že úplně jednoduchý princip (skládání fcí) je z nějakých důvodů potřeba řešit poměrně komplikovaně a vychází to z ještě komplikovanějšího formálního aparátu. Takže jsem po dlouhých úvahách došel k tomu, že je potřeba člověku prvně nějak neformálně ukázat, proč to je a k čemu to vlastně slouží - a teprve potom, až má nějakou mlhavou představu, o čem je vlastně řeč, ukázat prvně buď příklad, nebo velice zlehounka ten formalismus.
Mně třeba největší aha moment přinesla nějaká prezentace, kde týpek vysvětloval monády jako větvení vlakových kolejí - když nedojde k chybě, jede vlak dál touhle cestou, a když dojde, přejede přes tuhle vyhýbku... Bohužel to teď asi nedohledám, ale tohle byla fakt první věc, na které jsem vůbec chytl, o co vlastně teda jde.
...a když tohle člověk chytne, tak už potom dá ty příklady - ale imho by jako první měl vidět příklad rozepsaný pomocí lambd, BEZ do notace a těch speciálních operátorů. Pokud to totiž uvidí napsané v do notaci, získá falešný dojem, že tomu rozumí ("jasně, to je jako v imperativních jazycích") a je úplně mimo, protože nepochopil vůbec nic.
Prostě, mám pocit, že tohle téma může dobře vysvětlit jenom člověk, kterej nemá potřebu dělat chytrýho, ale naopak se rád sníží k "dětinským" příkladům, aby studenty látku efektivně naučil. A takových učitelů je bohužel na našich VŠ asi dost málo... Je to škoda. Zrovna tohle by si zasloužil znát každý mírně nadprůměrný programátor...
Jako chapu, ze ciste FP neni moc popularni. Ale vzhledem k tomu, jak se mu uspesne dari infiltrovat popularni jazyky, tak si myslim, ze minimalne jeden predmet v bakalari by si to zaslouzilo.
Určitě! Hele, upřímně, na Haskell jako takovej sere pes. Je to super obohacení, ale když ho nebudeš znát, nic moc se nestane. Co by ale každej měl znát (na škole minimálně "ochutnat"), je ten FP způsob myšlení - důraz na základní datové struktury a operace nad nimi, proudové zpracování, řetězení, atd. atd.
Ono totiž FP je úžasný způsob, jak si prakticky osahat ty datové struktury, o kterých se člověk učí v teorii...
Rozhodne nerazim nazor "Vsude ciste FP!". Treba prave ta Scala neni ani omylem cista (Haskellisti se na ni myslim celkem casto divaji shora) a FP se na nektere veci hodi mnohem vice, na jine naopak vice vynika imperativni pristup a OOP.
Myslím, že je úžasný koukat na to, jak Haskell razí principielní přístup bez výjimek a způsobuje si tím zpoustu problémů, které se nedají řešit jinak než relativně komplikovaně. A vedle něj jsou pragmatičtější jazyky, které jdou trochu "špinavější" cestou, ztrácí tím některé prima vlastnosti Haskellu, ale zase nemají ty jeho problémy a tímpádem se dají lehčeji naučit a líp se kombinují s tím, co člověk zná odjinud.
FP určitě do mainstreamu pronikat bude (už se to masově děje), ale řekl bych, že spíš v takové té pragmatičtější formě - jako volitelná součást, použitelná, pokud programátor chce, ale nesvazující ho tam, kde nechce.
Monády a jak je (ne)učit se tu už řešilo, ne? Vzhledem k tomu, že jsou začarované (jakmile je někdo pochopí, ztrácí tím automaticky schopnost je vysvětlovat, a něco na tom bude, normálně se mi celkem daří vysvětlovat lidem složitější koncepty jako entropii nebo modality apod., ale u monád mi to prostě nejde), to ani nemá moc smysl.
A teď vážně, buď je lze vysvětlit - někomu bez matematických základů - pomocí lambda výrazů (unit a bind) a jejich skládání, nebo na příkladech kolekcí (to mi ale nepřijde příliš vhodné). Žádnou magii bych v tom nehledal.
-
problém nastal, když jsem začal skládat dva jazyky, jeden "normální", druhý (tvořený příkazy break a continue) povolený jen v těle iterace jako součet s oním "normálním", pak je nutné použít stejnou interpretační funkci na dva různý typy
jestli to zní zmateně, tak to může být i tím, že já sám jsem
Aniž bych viděl kód - někdo tu linkoval http://www.cs.ru.nl/~W.Swierstra/Publications/DataTypesALaCarte.pdf (http://www.cs.ru.nl/~W.Swierstra/Publications/DataTypesALaCarte.pdf) - vypadá to jako dost podobné tomu, co děláš :)
já jsem to asi aji četl, ale myslím, že to neřeší můj problém (víc intepretrů/"backendů"), já sám ten problém momentálně neřeším, ten kód vypadá docela hnusně, ale funguje, až se mi jednou bude chtít, klidně ho zahodím a napíšu znovu a jinak
-
Mně třeba největší aha moment přinesla nějaká prezentace, kde týpek vysvětloval monády jako větvení vlakových kolejí -
Ten samý princip je použitý tady: http://www.zohaib.me/yet-another-what-is-a-monad-post/ Ale není to ten článek, co mi tenkrát přinesl ten aha moment :)
A zase ty monoidy :)
-
A zase ty monoidy :)
Jasně :) Ale odkazoval jsem to kvůli tomu obrázku s kolejema dole. Tenhle obrázek by imho měl každý zájemce o monády vidět jako první. A pak teprve vysvětlovat něco dalšího.
Monády a jak je (ne)učit se tu už řešilo, ne?
Ani ne. Minule jsme se bavili hlavně o tom, jestli je vůbec potřeba je znát k tomu, aby člověk mohl programovat IO v Haskellu.
-
A zase ty monoidy :)
Jasně :) Ale odkazoval jsem to kvůli tomu obrázku s kolejema dole. Tenhle obrázek by imho měl každý zájemce o monády vidět jako první. A pak teprve vysvětlovat něco dalšího.
Monády a jak je (ne)učit se tu už řešilo, ne?
Ani ne. Minule jsme se bavili hlavně o tom, jestli je vůbec potřeba je znát k tomu, aby člověk mohl programovat IO v Haskellu.
1) Tak aspoň jsou ty koleje názornější než burritos.
2) Pro IO asi nutně ne, ale IO je vůbec pofidérní, monády bych začínal učit od Identity a Maybe.
-
Jasně :) Ale odkazoval jsem to kvůli tomu obrázku s kolejema dole. Tenhle obrázek by imho měl každý zájemce o monády vidět jako první. A pak teprve vysvětlovat něco dalšího.
Ty koleje vypadají dobře. Teď mi zbývá jen pochopit, co mají s těmi monádami společného...
-
1) Tak aspoň jsou ty koleje názornější než burritos.
Burritos jsou nesmysl, tohle je názorný, korektní vysvětlení přímo k jádru pudla.
Kouknul jsi se na ten link? Kvůli maybe potřebuješ dvojnásobek funkcí, protože jak je to strikt, tak musíš řešit, že by se třeba ten druhý parametr musel vyhodnocovat dlouho. Takže oproti zcela triviální implimentaci Maybe v haskellu to je docela prohra.
Aha, ok. Na link jsem se předtím zběžně koukl, ale netušil jsem, co chceš říct, tak jsem nevěděl, co tam mám hledat :)
Máš pravdu, je to komplikace. Ale když máš default-lazy jazyk, tak je těch komplikací víc a jsou hlubší. Tohle je lahoda.
Navíc místo dvou funkcí můžeš použít (jak říká Radek) nějakou obálku typu future apod. Pořád je to řádově jednodušší než tanečky s IO monádou :)
-
Ty koleje vypadají dobře. Teď mi zbývá jen pochopit, co mají s těmi monádami společného...
Ty koleje vyjadřují tvůj základní cíl: chceš skládat funkce, které při úspěchu vrací typ X a pokračují ve zpracování, zatímco při neúspěchu vraxí typ Y a dál nepokračují. To je přesně to, k čemu monády slouží a je to triviální.
Potom další krok je, jak toho dosáhnout - no a dosáhneš toho vhodně zvolenými lambda funkcemi. To pořád ještě pochopí každý.
...no a pak teprve přichází ta magie: tenhle celý cirkus můžeš nadefinovat obecně, bez ohledu na konkrétní typy a tomu se říká monáda.
Snadný jak facka, ne?! ;)
-
Mně třeba největší aha moment přinesla nějaká prezentace, kde týpek vysvětloval monády jako větvení vlakových kolejí -
Ten samý princip je použitý tady: http://www.zohaib.me/yet-another-what-is-a-monad-post/ Ale není to ten článek, co mi tenkrát přinesl ten aha moment :)
Možná je to tento článek? https://fsharpforfunandprofit.com/rop
-
1) Tak aspoň jsou ty koleje názornější než burritos.
Burritos jsou nesmysl, tohle je názorný, korektní vysvětlení přímo k jádru pudla.
Jo, jsou nesmysl, ale ty koleje se taky nezdají, že by dostatečně vysvětlovaly různé aspekty monád, jen jeden (byť důležitý a často se vyskytující).
-
Ty koleje vypadají dobře. Teď mi zbývá jen pochopit, co mají s těmi monádami společného...
Ty koleje vyjadřují tvůj základní cíl: chceš skládat funkce, které při úspěchu vrací typ X a pokračují ve zpracování, zatímco při neúspěchu vraxí typ Y a dál nepokračují. To je přesně to, k čemu monády slouží a je to triviální.
Potom další krok je, jak toho dosáhnout - no a dosáhneš toho vhodně zvolenými lambda funkcemi. To pořád ještě pochopí každý.
...no a pak teprve přichází ta magie: tenhle celý cirkus můžeš nadefinovat obecně, bez ohledu na konkrétní typy a tomu se říká monáda.
Snadný jak facka, ne?! ;)
To je ale trochu zjednodušující, seznam je taky monáda a takto vysvětlit nejde, ne?
-
Ty koleje vypadají dobře. Teď mi zbývá jen pochopit, co mají s těmi monádami společného...
Ty koleje vyjadřují tvůj základní cíl: chceš skládat funkce, které při úspěchu vrací typ X a pokračují ve zpracování, zatímco při neúspěchu vraxí typ Y a dál nepokračují. To je přesně to, k čemu monády slouží a je to triviální.
[citation needed]
-
To je ale trochu zjednodušující, seznam je taky monáda a takto vysvětlit nejde, ne?
byl jste rychlejší, dále např. identity, reader, state
-
To je ale trochu zjednodušující, seznam je taky monáda a takto vysvětlit nejde, ne?
byl jste rychlejší, dále např. identity, reader, state
a taky IO
prostě říct, že monáda je jako tamto kolejiště je jako říct, že monáda je jako burrito
-
Ty koleje vypadají dobře. Teď mi zbývá jen pochopit, co mají s těmi monádami společného...
Ty koleje vyjadřují tvůj základní cíl: chceš skládat funkce, které při úspěchu vrací typ X a pokračují ve zpracování, zatímco při neúspěchu vraxí typ Y a dál nepokračují. To je přesně to, k čemu monády slouží a je to triviální.
[citation needed]
To je prostě jen jeden typ monády, Maybe nebo případně Error.
-
To je ale trochu zjednodušující, seznam je taky monáda a takto vysvětlit nejde, ne?
byl jste rychlejší, dále např. identity, reader, state
a taky IO
prostě říct, že monáda je jako tamto kolejiště je jako říct, že monáda je jako burrito
Tak ty koleje vysvětlují celkem hezky (polopaticky) právě to Maybe, ale nic jiného kolem monád. Možná že každá konkrétní monáda vyžaduje svou metaforu.
-
To je prostě jen jeden typ monády, Maybe nebo případně Error.
[/quote]
nebo Either nebo Except... zpracování chybových stavů tady ještě nikdo nezmínil jako problém haskellu
-
Tak ty koleje vysvětlují celkem hezky (polopaticky) právě to Maybe, ale nic jiného kolem monád. Možná že každá konkrétní monáda vyžaduje svou metaforu.
možná, že jeden z problémů je používání metafor
-
Tak ty koleje vysvětlují celkem hezky (polopaticky) právě to Maybe, ale nic jiného kolem monád. Možná že každá konkrétní monáda vyžaduje svou metaforu.
možná, že jeden z problémů je používání metafor
Nevím, mně přijdou taková porovnání celkem užitečná, i Einstein přece létal na paprsku světla (díky čemuž jsem kdysi dávno pochopil speciální teorii relativity) :)
-
To je prostě jen jeden typ monády, Maybe nebo případně Error.
Přesně tak, je to jedna instance obecného konceptu monády, kde se dá snadno ukázat, k čemu je to všechno dobré, 1. aniž by bylo potřeba napsat jedinou řádku kódu nebo jiného symbolického zápisu 2. aniž by to bylo zavádějící jako burritos.
-
možná, že jeden z problémů je používání metafor
To ale de facto není metafora. Je to v podstatě jenom jiný zápis (jiná symbolická reprezentace), stejně jako třeba graf je jiný zápis tabulky čísel.
-
možná, že jeden z problémů je používání metafor
To ale de facto není metafora. Je to v podstatě jenom jiný zápis (jiná symbolická reprezentace), stejně jako třeba graf je jiný zápis tabulky čísel.
Když řeknu, že monáda jsou koleje, tak to metafora je ;)
-
Když řeknu, že monáda jsou koleje, tak to metafora je ;)
No... ne tak docela. Asi nebudeš rozporovat tvrzení
Monáda je
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
x >> y = x >>= \_ -> y
fail :: String -> m a
fail msg = error msg
- takže co monáda je, jsem definoval pomocí nějaké posloupnosti symbolů.
...a každý ten řádek (řekněme "aspekt monády"?) můžeš zapsat právě pomocí toho "kolejového zápisu".
-
Když řeknu, že monáda jsou koleje, tak to metafora je ;)
No... ne tak docela. Asi nebudeš rozporovat tvrzení
Monáda je
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
x >> y = x >>= \_ -> y
fail :: String -> m a
fail msg = error msg
- takže co monáda je, jsem definoval pomocí nějaké posloupnosti symbolů.
...a každý ten řádek (řekněme "aspekt monády"?) můžeš zapsat právě pomocí toho "kolejového zápisu".
Tak lingvisticky to metafora je, ale nehodlám se hádat o slovíčka. Hlavní je, že omezíme-li se na ošetřování výjimek, je to celkem povedené vysvětlení. Ale co s jinými instancemi monád?
-
Ale co s jinými instancemi monád?
Jiné instance monád jde takhle vysvětlit/ilustrovat taky, ale už tam nebude taková hnedka zřejmá korespondence. Ale to není ani u toho klasického zápisu. Zkus někomu vysvětlit, že vytvoření singletonu x ->
-
Ale co s jinými instancemi monád?
Jiné instance monád jde takhle vysvětlit/ilustrovat taky, ale už tam nebude taková hnedka zřejmá korespondence. Ale to není ani u toho klasického zápisu. Zkus někomu vysvětlit, že vytvoření singletonu x ->
"Return" to je v Haskellu a je to matoucí. Normálně se tomu říká "unit", což je mnohem názornější vzhledem k pravidlům pro monády, ne?
-
Ale co s jinými instancemi monád?
Jiné instance monád jde takhle vysvětlit/ilustrovat taky, ale už tam nebude taková hnedka zřejmá korespondence. Ale to není ani u toho klasického zápisu. Zkus někomu vysvětlit, že vytvoření singletonu x ->
"Return" to je v Haskellu a je to matoucí. Normálně se tomu říká "unit", což je mnohem názornější vzhledem k pravidlům pro monády, ne?
return IMHO vznikl kvůli "do" notaci, jinak Monad je applicative functor, takže k dispozice je funkce "pure"
-
"Return" to je v Haskellu a je to matoucí. Normálně se tomu říká "unit", což je mnohem názornější vzhledem k pravidlům pro monády, ne?
Jj.
Chtěl jsem jenom říct, že pokud bys chtěl, můžeš na levou stranu koleje napsat "x" a na pravou "[ x ]" a bude to stejně dobře vysvětlovat/ilustrovat/popisovat i jinou instanci monády - List. A jakoukoliv další. Akorát u těch funkcí je to takový nejnázornější. Funkce je prostě takový kolejiště, po kterým jezdí vláčky - vlákna. List s kolejištěm tak intuitivně nesouvisí ;)
-
K tem burritos - Crockford (alespon v tom videu, co jsem videl) monady primo neprirovnava k burritos. Tam slo o to ze
Aby clovek pochopil monady, musi se nejdrive naucit Haskell a teorii kategorii.
je v podstate to stejne jako Aby clovek pochopil burritos, musi se nejdrive naucit spanelsky.
Rozhodne z toho nevyplyva, ze monady = burritos, "Haskell + teorie kategorii" = spanelstina nebo ze kdyz pochopim burritos, tak pochopim monady :D.
Prostě, mám pocit, že tohle téma může dobře vysvětlit jenom člověk, kterej nemá potřebu dělat chytrýho, ale naopak se rád sníží k "dětinským" příkladům, aby studenty látku efektivně naučil. A takových učitelů je bohužel na našich VŠ asi dost málo...
Tesat do kamene. Na VUT FIT v Brne jsme meli hodne ucitelu, kteri vucovani moc nezvladali (casto matematiky a ty silne teoreticke predmety; ale nebudu jmenovat, protoze tam byla alespon ta snaha). Bylo to neprijmne, protoze nejen, ze latka byla velmi narocna, navic nebyla vysvetlena moc dobre. No, a pak tam bylo nekolik presne takovych prednasejicich, o kterych pises (a shodou [?] okolnosti zrovna i ten FP predmet) - namachrovani, nikdy si neodpusti studenta shodit, otevrene davaji najevo (nekteri to i na rovinu rekli), jak vsichni studenti jsou flakaci a oni prednasi, jen aby meli penize na svuj vyzkum, at je tedy moc neotravujeme (mezi takove patrili pan Hruska a pan Kolar).
Je to škoda. Zrovna tohle by si zasloužil znát každý mírně nadprůměrný programátor...
Bych sel klidne i dal, kazdy prumerny by to mohl znat. Jako takove ty nejjednodussi veci typu operace nad kolekcemi si myslim nikomu neublizi a mohou hodne pomoci. I pokud jde o prumerneho programatora (coz neberu jako nic spatneho, sam se asi radim do teto kategorie, protoze z te teorie znam opravdu malo), ktery bude vyvojarit v Java/C#/JavaScriptu/PHP, tak snad vsude jsou dostupne budto primo v jazyku nebo pomoci externich knihoven nastroje umoznujici nejaky zakladni FP pristup (Java s verzi 8, C# ma ty divne metody ala SQL, JS treba Lodash, pro PHP jsem vygooglil functional-php).
Je opravdu k placi videt tu argumentovat C#pistu, jak ze pojmenovani "map, fold/reduce, filter" je nevhodne, ze C# to ma jako "select, aggregate, where" a jak je to mnohem nazornejsi a lip to vystihuje danou funkci...
FP určitě do mainstreamu pronikat bude (už se to masově děje), ale řekl bych, že spíš v takové té pragmatičtější formě - jako volitelná součást, použitelná, pokud programátor chce, ale nesvazující ho tam, kde nechce.
Hodne me prekvapilo, kdyz jsem se dozvedel o Redux (https://medium.com/google-developer-experts/angular-2-introduction-to-redux-1cf18af27e6e#.mvwrvm1js), ktere je v podstate FRP (ted se divam na ten clanek a ono to cerpa i z Elmu, nepsal jsi v tom?). Ve vodach Reactu je to myslim hodne popularni a uz i s Angularem to lidi pouzivaji.
Prave treba ta Scala, si myslim, je to pragrmaticke vyusteni - hybrid mezi OOP a FP. Pro lidi prechazejici z Javy lze psat skoro stejnym stylem ve Scale, jak byli zvykli v Jave (jsou tu vyjimky, treba pouze jeden hlavni konstruktor a ostatni musi pouzivat ten hlavni - to pro me byl ze zacatku maly sok). Ale postupne muzou zacit experimentovat s FP stylem s kolekcemi, ktere maji opravdu spoustu uzitecnych funkci, a jit jen tak daleko s FP, jak oni (nebo jejich tym) budou chtit.
-
Aha, ok. Na link jsem se předtím zběžně koukl, ale netušil jsem, co chceš říct, tak jsem nevěděl, co tam mám hledat :)
Máš pravdu, je to komplikace. Ale když máš default-lazy jazyk, tak je těch komplikací víc a jsou hlubší. Tohle je lahoda.
Navíc místo dvou funkcí můžeš použít (jak říká Radek) nějakou obálku typu future apod. Pořád je to řádově jednodušší než tanečky s IO monádou :)
Já fakt nechápu, co je za komplikace v default-lazy jazyku. Ano, když píšeš multithreadované programy s inter-thread komunikací, tak si pravděpodobně velmi rychle naběhneš na to, že je rozumné dělat strict datové struktury a na IORefy a spol budeš používat výhradně strict funkce. A... pokud tohle uděláš, tak v podstatě komplikace zmizely. Tak proč se zatěžovat nějakýma obálkama apod.? Fakt mi přijde, že v tomhle se z komára dělá velbloud. Třeba tohle používám naprosto běžně:
zip konecny_seznam [1..] To prostě strict nefunguje. Resp. funguje, když vymyslíš speciální definici listu, aby byl lazy.
Ale mě to přijde jako naprosto zbytečná komplikace - stejně jako v případě toho Maybe. Vůbec netuším, co tím získám (v případě Purescriptu vím, co tím získám - to, že výsledný kód je čitelný javascript, ale to mě v případě haskellu nezajímá).
-
"Return" to je v Haskellu a je to matoucí. Normálně se tomu říká "unit", což je mnohem názornější vzhledem k pravidlům pro monády, ne?
Jj.
Chtěl jsem jenom říct, že pokud bys chtěl, můžeš na levou stranu koleje napsat "x" a na pravou "[ x ]" a bude to stejně dobře vysvětlovat/ilustrovat/popisovat i jinou instanci monády - List. A jakoukoliv další. Akorát u těch funkcí je to takový nejnázornější. Funkce je prostě takový kolejiště, po kterým jezdí vláčky - vlákna. List s kolejištěm tak intuitivně nesouvisí ;)
To teda fakt nesouvisí :)
-
"Return" to je v Haskellu a je to matoucí. Normálně se tomu říká "unit", což je mnohem názornější vzhledem k pravidlům pro monády, ne?
Jj.
Chtěl jsem jenom říct, že pokud bys chtěl, můžeš na levou stranu koleje napsat "x" a na pravou "[ x ]" a bude to stejně dobře vysvětlovat/ilustrovat/popisovat i jinou instanci monády - List. A jakoukoliv další. Akorát u těch funkcí je to takový nejnázornější. Funkce je prostě takový kolejiště, po kterým jezdí vláčky - vlákna. List s kolejištěm tak intuitivně nesouvisí ;)
To teda fakt nesouvisí :)
tak analogie vláčku a seznamu není zas tak přitažená za vlasy (v monádě pak pure ~ zařazení vagonu nebo přistoupení člověka na nádraží)
-
"Return" to je v Haskellu a je to matoucí. Normálně se tomu říká "unit", což je mnohem názornější vzhledem k pravidlům pro monády, ne?
Jj.
Chtěl jsem jenom říct, že pokud bys chtěl, můžeš na levou stranu koleje napsat "x" a na pravou "[ x ]" a bude to stejně dobře vysvětlovat/ilustrovat/popisovat i jinou instanci monády - List. A jakoukoliv další. Akorát u těch funkcí je to takový nejnázornější. Funkce je prostě takový kolejiště, po kterým jezdí vláčky - vlákna. List s kolejištěm tak intuitivně nesouvisí ;)
To teda fakt nesouvisí :)
tak analogie vláčku a seznamu není zas tak přitažená za vlasy (v monádě pak pure ~ zařazení vagonu nebo přistoupení člověka na nádraží)
Aby pak ta metafora nebyla složitější než matematický zápis monád ;)
-
K tem burritos - Crockford (alespon v tom videu, co jsem videl) monady primo neprirovnava k burritos. Tam slo o to ze Aby clovek pochopil monady, musi se nejdrive naucit Haskell a teorii kategorii.
je v podstate to stejne jako Aby clovek pochopil burritos, musi se nejdrive naucit spanelsky.
Rozhodne z toho nevyplyva, ze monady = burritos, "Haskell + teorie kategorii" = spanelstina nebo ze kdyz pochopim burritos, tak pochopim monady :D.
Jo, ale minule jsme se ještě bavili o jiném článku, kde někdo humorně psal zhruba ve stylu "No jasně, a pak vžycky přijde nějakej franta, kterej začne monády přirovnávat k burritos a jiní lidi to pochopí tak, že monády voní" :)
Prostě metafory jsou nebezpečné, v tom s "v" souhlasím.
Je opravdu k placi videt tu argumentovat C#pistu, jak ze pojmenovani "map, fold/reduce, filter" je nevhodne, ze C# to ma jako "select, aggregate, where" a jak je to mnohem nazornejsi a lip to vystihuje danou funkci...
Ty a to mně třeba vůbec nepobuřuje. Mám radost z toho, že principy FP probublávají do jiných jazyků a těší mě, když se lidi naučí s tím pracovat, protože ty jiné jazyky jim nevytvářejí tolik bariér jako třeba právě ten Haskell. A vůbec mi nevadí ani když "obyčejný" programátor tu věc používá a o jejich FP kořenech vůbec neví. No a? Hlavně že si osvojil hezký FP přístup a píše pěkný kód, no ne? Co můžeš mít proti? ;)
Hodne me prekvapilo, kdyz jsem se dozvedel o Redux (https://medium.com/google-developer-experts/angular-2-introduction-to-redux-1cf18af27e6e#.mvwrvm1js), ktere je v podstate FRP (ted se divam na ten clanek a ono to cerpa i z Elmu, nepsal jsi v tom?). Ve vodach Reactu je to myslim hodne popularni a uz i s Angularem to lidi pouzivaji.
Ne, nedělal, nejsem webař, webařinu dělám jenom tak trochu. Když jsem si dělal letmej průzkum, co by se na frontendu dalo použít, viděl jsem mj. vtipnej článek o tom, že abyste v JS světě dosáhli všeho toho, co máte v Elmu musíte mít: React, Redux, bla bla bla... (ten seznam byl hrozně dlouhej) - a ten článek končil ve stylu "no a stojí vám to za to?!" ;)
Osobně jsem z JS světa nejvíc koketovat s Motorcycle, udělal jsem si pár pokusů v něm a v Elmu a hrozně moc mě odradilo to, že v Motorcycle z povahy věci (JS) může k chybě dojít kdekoli až v runtime, přičemž extenxivní používání různých futures a lazy eval atd. pak dělají tu chybu strašně těžko debugovatelnou. Tenhle dojem jsem hodně rychle nabyl a byl to definitivní důvod, proč jsem šel cestou Elmu. Možná na ten debug existují nějaký fígle, ale rychle jsem nabyl dojmu, že fakt nechci mít deset narovnáváků na pět ohýbáků a to všechno ještě dávat dohromady nějakým Gruntem nebo kýho čerta ;)
-
Ad vysvětlení Monadu - já mám pocit, že Monady nechápu pořádně doteď. Ty základní už jo, ale to je v podstatě díky tomu, že to člověk používá, naběhne si na spoustu vidlí a problému při konverzi typů, takže pak pochopí, jak to funguje. Na začátku jsem vůbec netušil, co je třeba tohle:
newtype Reader r a = Reader { runReader :: r -> a }A popravdě i teď bych nad tím musel drobet přemýšlet. Trochu líp jsem to pochopil, když jsem se snažil pochopit Free/Operational monad, ale pak přijde ContT a jsem zase v háji... Ta výhybková notace je hezká, jenomže když pak třeba pomocí Monadu implementujete něco jako Pipe (nebo třeba inkrementální parser), tak vám výhybka moc nepomůže.
Ještě jsem to nikomu nevysvětloval, ale v současné době mi nejlepší přijde analogie "programovatelného středníku" v "do" notaci. V podstatě tenhle článek http://apfelmus.nfshost.com/articles/operational-monad.html (http://apfelmus.nfshost.com/articles/operational-monad.html). A chápu, že začátečník se nad tím osype, ale pro mě tohle tak nějak vedlo k pochopení, co to Monad je. Aspoň trošku.
-
Aby pak ta metafora nebyla složitější než matematický zápis monád ;)
Přesně :)))))
Víte, sčítání, to je vlastně jako když vláček jede s polozataženými okýnky a do jednoho z těch okýnek vletí brambora s křídly a z druhé strany vypadne bábovka :)))
-
v současné době mi nejlepší přijde analogie "programovatelného středníku" v "do" notaci.
Mně přijde do notace naopak jako to největší zlo, které nikdy nemělo vzniknout a když už vzniklo, tak se na něho mělo zapomenout a kdokoli by se měl stydět ho zmínit ;)
-
Je opravdu k placi videt tu argumentovat C#pistu, jak ze pojmenovani "map, fold/reduce, filter" je nevhodne, ze C# to ma jako "select, aggregate, where" a jak je to mnohem nazornejsi a lip to vystihuje danou funkci...
Ty a to mně třeba vůbec nepobuřuje. Mám radost z toho, že principy FP probublávají do jiných jazyků a těší mě, když se lidi naučí s tím pracovat, protože ty jiné jazyky jim nevytvářejí tolik bariér jako třeba právě ten Haskell. A vůbec mi nevadí ani když "obyčejný" programátor tu věc používá a o jejich FP kořenech vůbec neví. No a? Hlavně že si osvojil hezký FP přístup a píše pěkný kód, no ne? Co můžeš mít proti? ;)
Jako ja jsem samozrejme taky rad, ze FP se pouziva i v tak popularnim jazyce, jako je C#. Ale mrzi me, ze pak pul vlakna o FP se resi, ze tohle C# mel davno a C#pisti se tam placaji po zadech, jak ostatni jazyky a pristupy jsou zaostale. Az po nekolika stranach, kdyz lidi postli dostatek dukazu (;D :'(), ze M$ opravdu to FP nevymyslel, tak jim doslo, ze asi nemaji pravdu a radeji prestali psat. Pritom stacilo mit na skole jeden predmet, kde si prakticky zkusili, jak ten map/filter/fold funguji, splacali projekt a uznali, ze je to zajimava alternativa k cyklu a zaradili si to v hlavne k potencionalnimu hlubsimu prozkoumani v budoucnu, az bude cas nebo mezi kandidaty na volitelne predmety.
-
Já fakt nechápu, co je za komplikace v default-lazy jazyku.
1. je to pro běžné programátory nepřirozené (většina jazyků je eager)
2. neodpovídá to tomu, jak skutečně počítač na hw úrovni funguje, takže se to de facto musí nějak mapovat jedno na druhé a každé mapování zhoršuje tvoji schopnost uvažovat nad vlastnostmi výsledku.
Normální eager evaluace funguje jako kuchařka: 1. vem vejce 2. rozklepni ho do misky 3. utři ho s cukrem
Nikomu se dobrovolně nechce přemýlet nad tím, jak to dopadne, když k rozklepnutí vajíčka dojde až po utření ;)
Třeba tohle používám naprosto běžně:
zip konecny_seznam [1..] To prostě strict nefunguje. Resp. funguje, když vymyslíš speciální definici listu, aby byl lazy.
Jo, to máš částečně pravdu. Ale 1. když to neznáš, tak to nepotřebuješ (je to "třešnička navíc") 2. stejně elegantně jako lazy evaluací se to dá řešit nějakou podobou streamu, která je intuitivně daleko pochopitelnější.
Ale mě to přijde jako naprosto zbytečná komplikace
Komplikace to je, ale nic nemůže být větší komplikace než nutnost pochopit IO monádu, abys mohl vypsat Hello, Mirek ;)
-
Ale mrzi me, ze pak pul vlakna o FP se resi, ze tohle C# mel davno a C#pisti se tam placaji po zadech, jak ostatni jazyky a pristupy jsou zaostale.
Tak co, no? Ty to víš, jak to je, tak se nad tím můžeš jenom pousmát, ne? ;)
-
Aby pak ta metafora nebyla složitější než matematický zápis monád ;)
Přesně :)))))
Víte, sčítání, to je vlastně jako když vláček jede s polozataženými okýnky a do jednoho z těch okýnek vletí brambora s křídly a z druhé strany vypadne bábovka :)))
:o To je citace z čeho?
-
Aby pak ta metafora nebyla složitější než matematický zápis monád ;)
Přesně :)))))
Víte, sčítání, to je vlastně jako když vláček jede s polozataženými okýnky a do jednoho z těch okýnek vletí brambora s křídly a z druhé strany vypadne bábovka :)))
:o To je citace z čeho?
Odtud: http://forum.root.cz/index.php?topic=13225.msg166129#msg166129 (http://forum.root.cz/index.php?topic=13225.msg166129#msg166129)
-
:o To je citace z čeho?
To je volná improvizace na tebou nadnesenou myšlenku :)
-
Jo, to máš částečně pravdu. Ale 1. když to neznáš, tak to nepotřebuješ (je to "třešnička navíc") 2. stejně elegantně jako lazy evaluací se to dá řešit nějakou podobou streamu, která je intuitivně daleko pochopitelnější.
??? tak když neznáš haskell a FP tak to taky nepotřebuješ... ale hlavně vůbec nechápu, co je na laziness tak složitého. Jakýkoliv strict program funguje úplně v pohodě v lazy prostředí. Opačně to neplatí. Ano, můžeš si udělat explicitní "thunk" jako v Purescriptu, případně s podporou jazyka (ocaml???), ale u člověka, by třeba s haskellem začínal, mi vůbec není jasné, proč by měl laziness řešit. Tak se tvař, že to je strict, výsledky z toho budou padat stejné. Kde je problém?
Nikomu se dobrovolně nechce přemýlet nad tím, jak to dopadne, když k rozklepnutí vajíčka dojde až po utření ;)
Jenomže to je monadická záležitost. Pure FP, kde je laziness vidět nejvíc, je spíš něco jako "projektový plán". A "krizový management" taky nevyhodnocuješ "eager", ale teprve až v momentě, kdy přijde krize.
v současné době mi nejlepší přijde analogie "programovatelného středníku" v "do" notaci.
Mně přijde do notace naopak jako to největší zlo, které nikdy nemělo vzniknout a když už vzniklo, tak se na něho mělo zapomenout a kdokoli by se měl stydět ho zmínit ;)
Když mě to vysvětlení, že to je programovatelný středník připadá, že to vlastně ani není analogie. A té do-notaci to prostě sedí. Ty příšernosti s "\x -> a >>= ..." jsem fakt nechápal. IMO když se začne s tím, že to je monoid (tedy programovatelný středník, asociativní ideálně jako a >> (b >> c)) a pak se to rozšíří o to vysvětlení, jak se přenáší hodnota z jednoho kusu výpočtu, tak to nepoužívá analogie a je to jednoduché na pochopení. Ale možná jsem ztratil schopnost to vysvětlit....
-
Jo, to máš částečně pravdu. Ale 1. když to neznáš, tak to nepotřebuješ (je to "třešnička navíc") 2. stejně elegantně jako lazy evaluací se to dá řešit nějakou podobou streamu, která je intuitivně daleko pochopitelnější.
??? tak když neznáš haskell a FP tak to taky nepotřebuješ... ale hlavně vůbec nechápu, co je na laziness tak složitého. Jakýkoliv strict program funguje úplně v pohodě v lazy prostředí. Opačně to neplatí. Ano, můžeš si udělat explicitní "thunk" jako v Purescriptu, případně s podporou jazyka (ocaml???), ale u člověka, by třeba s haskellem začínal, mi vůbec není jasné, proč by měl laziness řešit. Tak se tvař, že to je strict, výsledky z toho budou padat stejné. Kde je problém?
Nikomu se dobrovolně nechce přemýlet nad tím, jak to dopadne, když k rozklepnutí vajíčka dojde až po utření ;)
Jenomže to je monadická záležitost. Pure FP, kde je laziness vidět nejvíc, je spíš něco jako "projektový plán". A "krizový management" taky nevyhodnocuješ "eager", ale teprve až v momentě, kdy přijde krize.
v současné době mi nejlepší přijde analogie "programovatelného středníku" v "do" notaci.
Mně přijde do notace naopak jako to největší zlo, které nikdy nemělo vzniknout a když už vzniklo, tak se na něho mělo zapomenout a kdokoli by se měl stydět ho zmínit ;)
Když mě to vysvětlení, že to je programovatelný středník připadá, že to vlastně ani není analogie. A té do-notaci to prostě sedí. Ty příšernosti s "\x -> a >>= ..." jsem fakt nechápal. IMO když se začne s tím, že to je monoid (tedy programovatelný středník, asociativní ideálně jako a >> (b >> c)) a pak se to rozšíří o to vysvětlení, jak se přenáší hodnota z jednoho kusu výpočtu, tak to nepoužívá analogie a je to jednoduché na pochopení. Ale možná jsem ztratil schopnost to vysvětlit....
Slovo "monoid" se tady na fóru nevyslovuje (pokud teda - podle některých - nejsi autista) ;)
-
Ale jinak: Pro ty, kteří opravdu bazírují na výkonnosti tu máme https://ghc.haskell.org/trac/ghc/wiki/StrictPragma (https://ghc.haskell.org/trac/ghc/wiki/StrictPragma). Stačí to dát na začátek modulu (nebo do Cabalu) a jede se... ale osobně kromě snad nějakého výpočetního důvodu to fakt nemám důvod použít.
Mě spíš trápí to, že nevím, co přesně ten můj program kdy vyhodnocuje. Kromě zmíněného space leaku to komplikuje i profilování. (Na jednu stranu je hezké, že se Haskell inspiroval algebrou, na druhou stranu řadu lidí, co dělala algebru, nezajímala časová a prostorová složitost používaných technik, což se do Haskellu přeneslo a teď se to někteří lidé snaží opravit. Například u některých monád (nebo u některých implementací) ovlivňuje asociativita asymptotickou časovou složitost - například u monády strom (bind nahrazuje listy stromu jinými podstromy) se můžete dostat z lineární na kvadratickou).
BTW ta výhoda s nevyhodnocením defaultní hodnoty v Haskellu je vyvážena nutnostní duplikovat funkce pro monády - například, pokud by výpočet defaultní hodnoty používal vstup a výstup (tj. byl v IO monádě), tak budu potřebovat speciální verzi fromMaybe.
Další věc jsou líné datové struktury, jejichž vyhodnocování může způsobovat vedlejší efekty - hádám, že to v Haskellu nebude jednodušší než třeba ve Scale.
Tak to je dobře (GoodThing), že to neslinkuje :) Proto taky GHC varuje před OrphanInstances a když jsou v knihovně OrphanInstances tak to je BadThing.
Vytvářet OrphanInstances mi přijde jako hlavní výhoda typových tříd oproti rozhraním z Javy a spol.
Nestává se vám, že máte dvě knihovny - jedna definuje typovou třídu, druhá typ a vy zrovna potřebujete instanci typové třídy z první knihovny pro typ z druhé knihovny?
On na první pohled ten newtype vypadá "kostrbatě", ale mě třeba připadá, že jinak to stejně nejde - nebylo by to "composable".
Problém je, že po zabalení do newtype už ta zabalená hodnota nejde použít v místě, kde je vyžadován původní typ (musím hodnotu zase rozbalit). Ve Scale na to jde použít tagování - například od typu Int přejdu k Int @@ Product, což je podtyp Int, tj. mohu to použít kdekoliv je vyžadován Int.
Třeba jsem řešil, že chci ve FromJSON struktuře deserializovat něco jinak, než je v default typeclass - třeba některá čísla tam jsou hexadecimálně ve stringu. A nevím, jak by se tohle řešilo - přiřadit typeclass konkrétní položce v recordu? A pak ta hodnota bude i s tou typeclass někde cestovat?
Ve Scale by se to řešilo tak, že uděláte typovou třídu pro typ Int a pro typ Int @@ Hex - v záznamu pak použiji typ Int @@ Hex pro pole, která jsou hexadecimálně. Jelikož Int @@ Hex je podtyp Int, jde s ním pracovat stejně jako s Intem.
Instance typových tříd ve Scale jsou hodnoty nebo funkce označené klíčovým slovem implicit a kompilátor je hledá v tzv. implicit scope - když chci použít jinou instanci, stačí například importovat modul.
-
Jakýkoliv strict program funguje úplně v pohodě v lazy prostředí.
Tomu nerozumím. Možná myslíš jakýkoli program bez vedlejších efektů? Nebo jakýkoli program přeložený do nějakého aparátu, který v lazy prostředí umožňuje vedlejší efekty řadit? A jak bych si vůbec měl představit program v Pythonu "fungující v prostředí" Haskellu?
Jenomže to je monadická záležitost. Pure FP, kde je laziness vidět nejvíc, je spíš něco jako "projektový plán". A "krizový management" taky nevyhodnocuješ "eager", ale teprve až v momentě, kdy přijde krize.
Nevím, co myslíš tím "monadická záležitost". Haskell prostě může cokoli vyhodnotit kdykoli, takže kdybych dal za sebe
print "a"
print "b"
tak mi to může vypsat "a\nb\n" i "b\na\n".
Protože to pořadí chci zachovat, musím udělat tu opičku s tím, že oba printy uzavřu do lambd, trochu to ošperkuju tak, aby mi typový systém zaručil spuštění ve správném pořadí a to, jak je to vlastně udělaný, je obtížný pochopit, zatímco v eage jazyce je to prostě a jednoduše
print "a"
print "b"
bez jakékoli další bižuterie.
Tomu říkáš "co je na tom za komplikaci"?
Když mě to vysvětlení, že to je programovatelný středník připadá, že to vlastně ani není analogie.
Není to analogie. Mně na do notaci vadí to, že uměle zavádí pojmy z eager imperativního programování do prostředí, kde mají úplně jiný význam. Prostě return není žádný return a říkat mu return nadělá víc škody než užitku.
-
Slovo "monoid" se tady na fóru nevyslovuje (pokud teda - podle některých - nejsi autista) ;)
Ty můžeš, o tobě to stejně všichni už víme ;)
-
Jakýkoliv strict program funguje úplně v pohodě v lazy prostředí.
Tomu nerozumím. Možná myslíš jakýkoli program bez vedlejších efektů? Nebo jakýkoli program přeložený do nějakého aparátu, který v lazy prostředí umožňuje vedlejší efekty řadit? A jak bych si vůbec měl představit program v Pythonu "fungující v prostředí" Haskellu?
program v haskellu napsaný tak jako by haskell byl eager bude fungovat v lazy haskellu stejně jako v tom teoretickém eager
-
Jakýkoliv strict program funguje úplně v pohodě v lazy prostředí.
Tomu nerozumím. Možná myslíš jakýkoli program bez vedlejších efektů? Nebo jakýkoli program přeložený do nějakého aparátu, který v lazy prostředí umožňuje vedlejší efekty řadit? A jak bych si vůbec měl představit program v Pythonu "fungující v prostředí" Haskellu?
program v haskellu napsaný tak jako by haskell byl eager bude fungovat v lazy haskellu stejně jako v tom teoretickém eager
I když se za běhu rozštěpí do více vláken?
-
Jakýkoliv strict program funguje úplně v pohodě v lazy prostředí.
Tomu nerozumím. Možná myslíš jakýkoli program bez vedlejších efektů? Nebo jakýkoli program přeložený do nějakého aparátu, který v lazy prostředí umožňuje vedlejší efekty řadit? A jak bych si vůbec měl představit program v Pythonu "fungující v prostředí" Haskellu?
program v haskellu napsaný tak jako by haskell byl eager bude fungovat v lazy haskellu stejně jako v tom teoretickém eager
Tak. Prostě když budu mít kód přeložený se Language Strict, StrictData a tu direktivu odstranim, tak to bude davat stejne vysledky. Tak proč by to meli začátečníci řešit?
-
Jakýkoliv strict program funguje úplně v pohodě v lazy prostředí.
Tomu nerozumím. Možná myslíš jakýkoli program bez vedlejších efektů? Nebo jakýkoli program přeložený do nějakého aparátu, který v lazy prostředí umožňuje vedlejší efekty řadit? A jak bych si vůbec měl představit program v Pythonu "fungující v prostředí" Haskellu?
program v haskellu napsaný tak jako by haskell byl eager bude fungovat v lazy haskellu stejně jako v tom teoretickém eager
I když se za běhu rozštěpí do více vláken?
Ano.
-
Jakýkoliv strict program funguje úplně v pohodě v lazy prostředí.
Tomu nerozumím. Možná myslíš jakýkoli program bez vedlejších efektů? Nebo jakýkoli program přeložený do nějakého aparátu, který v lazy prostředí umožňuje vedlejší efekty řadit? A jak bych si vůbec měl představit program v Pythonu "fungující v prostředí" Haskellu?
program v haskellu napsaný tak jako by haskell byl eager bude fungovat v lazy haskellu stejně jako v tom teoretickém eager
Tak. Prostě když budu mít kód přeložený se Language Strict, StrictData a tu direktivu odstranim, tak to bude davat stejne vysledky.
A nemůže se stát, že program začne padat na přetečení zásobníku?
-
A nemůže se stát, že program začne padat na přetečení zásobníku?
nenapadá mě jak, máte takový příklad?
-
Z Wikipedie hesla Leibnitz a filosofie :-) :
Co jsou monády?
Monády jsou body. To znamená, že vlastní prazáklad jsoucna jsou bodové substance. Tento základ tedy není kontinuum. Zdá se to být v rozporu se smyslovou zkušeností, látky se nám zdají jako rozlehlé kontinuum. Leibniz tvrdí, že tento smyslový dojem klame.
Monády jsou síly, silová centra. Těleso není nic jiného než komplex bodových silových center.
Monády jsou duše. Bodové monády jsou oduševnělé, ale liší se ve schopnosti představ – percepce. Nejnižší monády jsou jakoby ve stavu snu či omámení, mají jen temné, nevědomé představy. Prostřední monády mají jakési mlhavé představy, jsou schopné odlišit své představy od ostatních, ale už ne rozlišovat jednotlivé představy mezi sebou. Vyšší monády, jako je lidská duše, mají vědomí, schopnost percepce a apercepce – sebeuvědomění, reflexe apod. A nejvyšší monáda, bůh, má nekonečné vědomí, je vševědoucí.
Monády jsou individua. Neexistují dvě stejné monády. Každá monáda, od té nejjednodušší k nejsložitější, má své nezaměnitelné místo, každá zrcadlí univerzum svým vlastním, jedinečným způsobem a každá je potenciálně, co do možnosti, zrcadlem univerza. Monády jsou individua také v tom smyslu, že jsou navenek uzavřeny – nemají žádná „okna“ – neovlivňují se.
A z toho čerpá i FP, monáda je jedinečná "šablona" nějakého projevu reálného světa.
-
A nemůže se stát, že program začne padat na přetečení zásobníku?
nenapadá mě jak, máte takový příklad?
jasně, zřídka čtený, často inkrementovaný čítač
-
A nemůže se stát, že program začne padat na přetečení zásobníku?
nenapadá mě jak, máte takový příklad?
jasně, zřídka čtený, často inkrementovaný čítač
Ano, něco na ten způsob. Vycházel jsem z rozdílu mezi
foldl (+) 0 [1..1000000]
foldl' (+) 0 [1..1000000]
kde oba řádky sčítají čísla ze seznamu. První však spadne na přetečení zásobníku, druhý projde.
-
mi se vybavilo toto:
https://hackage.haskell.org/package/base-4.8.2.0/docs/Data-IORef.html#v:modifyIORef
-
Slovo "monoid" se tady na fóru nevyslovuje (pokud teda - podle některých - nejsi autista) ;)
Ty můžeš, o tobě to stejně všichni už víme ;)
Máš zajímavou definici opaku "lempl na matematiku", ale brát ti ji nebudu ;)
-
BTW ta výhoda s nevyhodnocením defaultní hodnoty v Haskellu je vyvážena nutnostní duplikovat funkce pro monády - například, pokud by výpočet defaultní hodnoty používal vstup a výstup (tj. byl v IO monádě), tak budu potřebovat speciální verzi fromMaybe.
To je ale spíš dané pure/non-pure rozdělením než laziness...
Další věc jsou líné datové struktury, jejichž vyhodnocování může způsobovat vedlejší efekty - hádám, že to v Haskellu nebude jednodušší než třeba ve Scale.
No v Haskellu vyhodnocování žádné vedlejší efekty nemá... (teda pominu-li historické chyby typu hGetContents a jsem spoluzodpovědný za to, že se to zrušilo z jedné další knihovny).
Vytvářet OrphanInstances mi přijde jako hlavní výhoda typových tříd oproti rozhraním z Javy a spol.
Nestává se vám, že máte dvě knihovny - jedna definuje typovou třídu, druhá typ a vy zrovna potřebujete instanci typové třídy z první knihovny pro typ z druhé knihovny?
Stává, a mělo by to být definované v jedné z těch 2 knihoven - jenomže občas na to autor nemyslel a jindy se mu nechtělo přidělávat si dependence. Pokud píšu výsledný program, tak ty orphan instance udělám, ale v knihovnách by Orphan být neměly.
Ve Scale by se to řešilo tak, že uděláte typovou třídu pro typ Int a pro typ Int @@ Hex - v záznamu pak použiji typ Int @@ Hex pro pole, která jsou hexadecimálně. Jelikož Int @@ Hex je podtyp Int, jde s ním pracovat stejně jako s Intem.
No, jenže co když mám podstrukturu, kterou jednou chci deserializovat jako Hex, jednou jako decimal? Jde to @@Hex s hodnotou, nebo je to vázané na strukturu? Možná je to nějak vymyšlené, ale numím si to úplně představit.
-
program v haskellu napsaný tak jako by haskell byl eager bude fungovat v lazy haskellu stejně jako v tom teoretickém eager
No ale co by to mělo dokazovat? Haskell je složitý, protože je lazy a ty lazy obraty by fungovaly i kdyby lazy nebyly?! No to dá rozum, na tom není nic zajímavého. Zajímavé je to, že ty obraty jsou složité, ABY mohly být lazy. Pokud jazyk lazy není, složité obraty nepotřebuje. To je odpověď na tu otázku "čemu vadí, že je lazy".
-
Nevím, co myslíš tím "monadická záležitost". Haskell prostě může cokoli vyhodnotit kdykoli, takže kdybych dal za sebe
print "a"
print "b"
tak mi to může vypsat "a\nb\n" i "b\na\n".
Nemůže. Protože to za sebe dáš akorát tak v IO monadu, jinde ne. Musíš trošičku pochopit IO monad (spíš rozdíl mezi pure a non-pure, kdy použít let a kdy <-). Lazy jsou pure výpočty, efekty jsou vždy explicitně řazeny. Takže IMO to prostě nemá vůbec žádný smysl řešit.
Tomu říkáš "co je na tom za komplikaci"?
Tak když vezmu třeba příklad, co tady byl dán:
foldl (+) 0 [1..1000000]
foldl' (+) 0 [1..1000000]
Takže faktem je, že v obou případech je potřeba, aby to pole byl vlastně Stream. Protože Haskell umí použít Fusion a to pole nealokovat. Takže v případě Strict jazyka nastává otázka, jestli tyhle věci mají být všechny Stream, nebo naopak všechny pole.
A k foldl tu máme ještě foldr - problém je, že foldl má smysl jenom strict a foldr zase typicky lazy. A to nemá nic společného s jazykem, ale spíš s tím, jak ty funkce pojmenovat. Z hlediska konzistence to takhle dává hodně smysl, z hlediska začátečníka to je trochu problém.... (možná by to chtělo udělat foldl depracated, aby aspoň varoval).
Takže ve výsledku je v haskellu všechno lazy s tím, že těch pár příkladů, kde to dává smysl, to můžeš snadno anotovat jako strict (např. maybe prostě striktně smysl nedává) vs. strict jazyky, kde vlastně na první pohled netušíš, jestli je funkce strict nebo lazy, nebo to máš poseté variantami funkcí a la Maybe v purescriptu.
No ale co by to mělo dokazovat? Haskell je složitý, protože je lazy a ty lazy obraty by fungovaly i kdyby lazy nebyly?! No to dá rozum, na tom není nic zajímavého. Zajímavé je to, že ty obraty jsou složité, ABY mohly být lazy. Pokud jazyk lazy není, složité obraty nepotřebuje. To je odpověď na tu otázku "čemu vadí, že je lazy".
No mě teda Maybe v haskellu připadne zcela triviální. Maybe v purescriptu má 2x tolik funkcí, aby obsloužilo totéž.
Hlavně ale myslel jsem to opačně: strict kód funguje jako lazy bez problémů (minus sem tam problém se stack overflow a space leaky), lazy kód ve strict prostředí fungovat vůbec nemusí (a performance taky může být problém, jen jinde). Takže člověk, který s tím jazykem začíná, prostě to, že to je lazy vůbec řešit nemusí. Může psát strict kód a chová se to přesně tak, jak by čekal. S foldl stack overflow jsem se nepotkal, s lazy modifyIORef ano a člověk se aspoň něco naučí :) Ale fakt se tohle přehání...
-
Co jsou monády?
...
Monády jsou individua. Neexistují dvě stejné monády. Každá monáda, od té nejjednodušší k nejsložitější, má své nezaměnitelné místo, každá zrcadlí univerzum svým vlastním, jedinečným způsobem a každá je potenciálně, co do možnosti, zrcadlem univerza. Monády jsou individua také v tom smyslu, že jsou navenek uzavřeny – nemají žádná „okna“ – neovlivňují se.
Dalo by se z toho velmi zjednodušeně dovodit, že monády tvoří uzly, zatímco funkce tvoří hrany?
-
Ve Scale by se to řešilo tak, že uděláte typovou třídu pro typ Int a pro typ Int @@ Hex - v záznamu pak použiji typ Int @@ Hex pro pole, která jsou hexadecimálně. Jelikož Int @@ Hex je podtyp Int, jde s ním pracovat stejně jako s Intem.
No, jenže co když mám podstrukturu, kterou jednou chci deserializovat jako Hex, jednou jako decimal? Jde to @@Hex s hodnotou, nebo je to vázané na strukturu? Možná je to nějak vymyšlené, ale numím si to úplně představit.
Je to vázané na strukturu. V tomto případě tedy lze udělat dvě instance typové třídy pro daný typ a vždy si zajistit, aby v implicit scope byla ta správná.
-
Je to akademický jazyk bez podpory imperativního programování, je to dobré na hraní si, ale v praxi je k ničemu.
-
Je to akademický jazyk bez podpory imperativního programování, je to dobré na hraní si, ale v praxi je k ničemu.
Ty si taky v praxi k ničemu ... je to "pure" FP jazyk, co by tam mělo co dělat imperativní paradigma trotle? BTW ono tam skutečně je díky IO monádě, proto jsem to pure dal do uvozovek ... A tebe by taky chtělo dát do uvozovek
-
Je to akademický jazyk bez podpory imperativního programování, je to dobré na hraní si, ale v praxi je k ničemu.
Ty si taky v praxi k ničemu ... je to "pure" FP jazyk, co by tam mělo co dělat imperativní paradigma trotle? BTW ono tam skutečně je díky IO monádě, proto jsem to pure dal do uvozovek ... A tebe by taky chtělo dát do uvozovek
FP bude mít smysl, až někdo vymyslí rozumnou notaci, přijatelnou má zatím jen clojure, ale i tak je to trochu znásilňování univerzální lispovské formy.
Jinak je problém, že v hlavě máte tuším, že cca 7 paměťových registrů, což na impertivní paradigma stačí, na FP ne, protože v něm pracujete se soubory pravidel, které se vzájemně mohou ovlivňovat, takže odvodit si chování části reálného programu je nemožné, budou vám v hlavě chybět registry, nebudete mít možnost to porovnat najednou.
viz toto:
x := z + 1
kde kontext je schovaný v z, takže výraz je představitelný najednou, jen za z si dosazujete různé hodnoty, mozek výraz nemusí číst znovu, udržuje ho v pracovních registrech, proces dosazování řídí mozek, vytváří si vlastní asociace na základě předchozího čtení programu. Což je přirozený a z hlediska mozku optimalizovaný proces, odpovídající i běžnému vnímání.
oproti tomuto:
f =
a > 100: x := 200
a < 100: x := 100
a = 80: x := 20
kde kontext je vyjádřen explicitně a výraz jako celek je nepředstavitelný, pokud s ním pracujete, musíte porovnávat několikrát jednotlivé řádky a v hlavě přesouvat informace do a z pracovních "registrů", kterých se vám nedostává (nejste schopen si představit jednu entitu na dvou místech zároveň), celý proces je navíc řízen textem programu, takže mozek ho musí číst a analyzovat opakovaně, což unavuje, protože nemůže využívat vlastních optimalizovaných asociací.
FP má budoucnost, ale až v době, kdy se stroje budou programovat samy.
Narazil jsem na zajímavou věc, kdy někdo považuje identifikátor x z výrazu int x := 1 za proměnnou a nikoliv za pojmenování místa v paměti počítače a x nechápe jako stav, kterým tudíž je, ale jako hodnotu.
-
Jinak je problém, že v hlavě máte tuším, že cca 7 paměťových registrů, což na impertivní paradigma stačí, na FP ne, protože v něm pracujete se soubory pravidel, které se vzájemně mohou ovlivňovat, takže odvodit si chování části reálného programu je nemožné, budou vám v hlavě chybět registry, nebudete mít možnost to porovnat najednou.
Sakra u vás je taky problém poznat, že si děláte srandu ;D
-
Jinak je problém, že v hlavě máte tuším, že cca 7 paměťových registrů, což na impertivní paradigma stačí, na FP ne, protože v něm pracujete se soubory pravidel, které se vzájemně mohou ovlivňovat, takže odvodit si chování části reálného programu je nemožné, budou vám v hlavě chybět registry, nebudete mít možnost to porovnat najednou.
Sakra u vás je taky problém poznat, že si děláte srandu ;D
No on je to legrace jen napůl, viz wikipedie, krátkodobá paměť:
Krátkodobá paměť
Krátkodobá paměť, (nebo také paměť pracovní, operativní) je vědomá aktivní část paměti, ve které se odehrává většina psychických procesů (např. řešení aktuálních problémů). Zpracovávají se v ní informace dodané senzorickou pamětí a informace vyvolané z paměti dlouhodobé, která není dostupná vědomě. Krátkodobá paměť dokáže uchovat vjemy smyslových orgánů a emoce pomocí přeměny (kódování) v mentální reprezentace. Ty může paměť dále zpracovávat a uchovávat.
Krátkodobá paměť je omezena na 5–9 prvků (tzv. magické číslo 7±2), které při zamezení opakování uchová na 15–20 sekund. Kapacitu lze zvýšit spojováním prvků do logických celků (např. mnemotechnické pomůcky). Pro zachování informace v krátkodobé paměti je třeba si informaci opakovat (tzv. fonologická smyčka), jinak je paměťová stopa nenávratně ztracena.
Tříjednotkový model krátkodobé paměti udává 3 mechanismy zpracování:
fonologická smyčka – dočasně ukládá zvukové a řečové informace
vizuoprostorový náčrtník – dočasně ukládá vizuálně prostorové informace
centrální výkonnostní smyčka – třídí a specifikuje krátkodobé informace
-
No on je to legrace jen napůl, viz wikipedie, krátkodobá paměť:
Tak pochopil jsem, že si děláte srandu v tom zbytku :) Ale co ti chudáci, co s FP nikdy nepřišli do styku? Ti by tomu skoro mohli věřit...
-
No on je to legrace jen napůl, viz wikipedie, krátkodobá paměť:
Tak pochopil jsem, že si děláte srandu v tom zbytku :) Ale co ti chudáci, co s FP nikdy nepřišli do styku? Ti by tomu skoro mohli věřit...
Zde máte příklad z Haskellu: (bohužel na GitHubu jsem nenarazil na reálnou aplikaci, jen školometské příklady)
zipWith' :: (a -> b -> c) -> [a] -> -> [c]
zipWith' _ [] _ = []
zipWith' _ _ [] = []
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
Při jeho analýze setrváváte neustále ve fonologické smyčce. Detailněji viz zde https://cs.wikipedia.org/wiki/Baddeleyho_model_pracovn%C3%AD_pam%C4%9Bti
Imperativní program zvládnete analyzovat po řádku, jen s kontextem ze střednědobé paměti, který se vybavuje automaticky. Proto je taky imperativní paradigma oblíbenější.
-
Už tu zas Ivan s vážnou tváří tvrdí, že je snazší uvažovat o kódu se side effecty?
-
Už tu zas Ivan s vážnou tváří tvrdí, že je snazší uvažovat o kódu se side effecty?
Známé nebezpečí si dovedete pohlídat, takže side efektům se dá vyhnout, nebo s nimi počítat. Jinak side effecty máte i v FP, jen v trochu jíné formě, například zde:
f =
a > 100: x := 200
a < 100: x := 100
a = 80: x := 20
Bude-li těch pravidel více, snadno přehlédnete, že první dvě pokrývají celý rozsah, ale třetí je výjimka z tohoto pravidla. Pokud to nebude v rámci jednoho příkazu, může dojít k vyhodnocení podmínek v různém pořadí a to povede na těžko odhalitelné chyby s podobným chováním jako side effect.
Jinak proti FP nic nemám, jen mi nevyhovuje jeho notace, s tím by se mělo něco udělat. Je na tom vidět, že jde o znásilňování principů z filosoficky odlišného imperativního paradigmatu.
A to, že lidský mozek nativně pracuje s kontextem a tudíž i se side effecty je fakt. Výhoda FP je právě v tom, že je to koncept, který je jiný, nekopíruje lidské myšlení, jde spíše proti němu, sází na formalizaci, která usnadňuje dokazování, ale znesnadňuje čtení a rychlé pochopení. Ono nikdy není nic zadarmo.
-
Ten kus kódu, co jsi pastnul, ti přijde jako funkcionální?
-
Ten kus kódu, co jsi pastnul, ti přijde jako funkcionální?
Je to z praxe v Haskellu. tak to bude vypadat, když se masově rozšíří. A pravidla jsou jen na tři řádky (tučně), v praxi budou třeba na 100 řádků (daňová pravidla)
funkcionálně můj předchozí příklad
list = map(lambda x: 200 if x > 100 else x, map(lambda x: 100 if x < 100 else x, list))
a někde v programu před předchozí sekvencí
list = map(lambda x: 200 if x = 80 else x, list)
a ve výsledku se budete divit, že vám někde mizí hodnoty 80, obdoba implicitního side effectu jako vyšitá.
-
A to je v čem horší než stejný případ v Pascalu, když si pošleš do funkce data, která sis před tím nabořil?
Je to naopak lepší v tom, že potenciálně nabořená místa nutně znáš
(jinak to, co popisuješ je problém, ale ne side effect. A všude jinde je ten problém horší. Nemluvě o tom strašáku budoucích prasat v haskellu.)
-
Ohledně otázky, zdali je pro člověka přirozenější funkcionální nebo imperativní paradigma je myslím odpověď jasná. Stačí nebejt "provinční" a zvednout hlavu od monitoru.
V jaké formě je psaná každá kuchařka? Ve stylu:
= rozděl vejce na žloutek a bílek
= bílek utři na sníh
= přimíchej tam žloutek, cukr a mouku a olej
= nalejte do formy
= pečte hodinu při 200 stupních
Nebo stylem:
= bílek a žloutek získáte rozdělením vejce
= sníh získáte ušleháním bílku
= těsto na bábovku získáte smíšením sněhu, žloutku, cukru a mouky a oleje
= neupečenou bábovku získáte nalitím těsta do formy
= bábovku získáte hodinovým pečením neupečené bábovky při 200 stupních.
(ideálně ještě nikoliv tomto pořadí :-))
?
FP má nemálo výhod - ale rozhodně to není způsob, jak lidé přirozeně přemýšlí.
Co se týče vysvětlení monád, tak nejlepší text, co jsem našel, mi přijde tento:
https://wiki.haskell.org/IO_inside
(teda doufám, že monády chápu správně, v haskellu jsem dělal jen zkoušku na VŠ :-)
-
Je to akademický jazyk bez podpory imperativního programování, je to dobré na hraní si, ale v praxi je k ničemu.
Ty si taky v praxi k ničemu ... je to "pure" FP jazyk, co by tam mělo co dělat imperativní paradigma trotle? BTW ono tam skutečně je díky IO monádě, proto jsem to pure dal do uvozovek ... A tebe by taky chtělo dát do uvozovek
FP bude mít smysl, až někdo vymyslí rozumnou notaci, přijatelnou má zatím jen clojure, ale i tak je to trochu znásilňování univerzální lispovské formy.
Jinak je problém, že v hlavě máte tuším, že cca 7 paměťových registrů, což na impertivní paradigma stačí, na FP ne, protože v něm pracujete se soubory pravidel, které se vzájemně mohou ovlivňovat, takže odvodit si chování části reálného programu je nemožné, budou vám v hlavě chybět registry, nebudete mít možnost to porovnat najednou.
viz toto:
x := z + 1
kde kontext je schovaný v z, takže výraz je představitelný najednou, jen za z si dosazujete různé hodnoty, mozek výraz nemusí číst znovu, udržuje ho v pracovních registrech, proces dosazování řídí mozek, vytváří si vlastní asociace na základě předchozího čtení programu. Což je přirozený a z hlediska mozku optimalizovaný proces, odpovídající i běžnému vnímání.
oproti tomuto:
f =
a > 100: x := 200
a < 100: x := 100
a = 80: x := 20
kde kontext je vyjádřen explicitně a výraz jako celek je nepředstavitelný, pokud s ním pracujete, musíte porovnávat několikrát jednotlivé řádky a v hlavě přesouvat informace do a z pracovních "registrů", kterých se vám nedostává (nejste schopen si představit jednu entitu na dvou místech zároveň), celý proces je navíc řízen textem programu, takže mozek ho musí číst a analyzovat opakovaně, což unavuje, protože nemůže využívat vlastních optimalizovaných asociací.
FP má budoucnost, ale až v době, kdy se stroje budou programovat samy.
Narazil jsem na zajímavou věc, kdy někdo považuje identifikátor x z výrazu int x := 1 za proměnnou a nikoliv za pojmenování místa v paměti počítače a x nechápe jako stav, kterým tudíž je, ale jako hodnotu.
Zajímavá úvaha, nicméně nepříliš relevantní řekl bych, v imperativním paradigmatu se také krátkodobá paměť dost napíná.
Jak je na tom z tohoto pohledu logické programování?
-
Ohledně otázky, zdali je pro člověka přirozenější funkcionální nebo imperativní paradigma je myslím odpověď jasná. Stačí nebejt "provinční" a zvednout hlavu od monitoru.
V jaké formě je psaná každá kuchařka? Ve stylu:
= rozděl vejce na žloutek a bílek
= bílek utři na sníh
= přimíchej tam žloutek, cukr a mouku a olej
= nalejte do formy
= pečte hodinu při 200 stupních
Nebo stylem:
= bílek a žloutek získáte rozdělením vejce
= sníh získáte ušleháním bílku
= těsto na bábovku získáte smíšením sněhu, žloutku, cukru a mouky a oleje
= neupečenou bábovku získáte nalitím těsta do formy
= bábovku získáte hodinovým pečením neupečené bábovky při 200 stupních.
(ideálně ještě nikoliv tomto pořadí :-))
?
FP má nemálo výhod - ale rozhodně to není způsob, jak lidé přirozeně přemýšlí.
a jak si vysvětlujete matematiku?
-
Logické programování ještě hůře, proto se neprosadilo skoro vůbec. To jsou vlastně už jen kolekce pravidel nesvázaných pořadím vykonávání. Rozhodnout pak, zda nějaký "jazyk" vstupních dat jim odpovídá, se už jen tak nedá. Pak to, zda program dělá to co má, je založeno už jen na víře.
Je to podobná situace jako v účetnictví, kdy v určitém momentu musíte věřit, že je to dobře zaúčtované, protože zkontrolovat to, znamená udělat ho celé znovu, s dalšími novými chybami.
Ostatně evoluce přijala podobný kompromis, imperativní paradigma, které se projevuje v tom, že realitu vnímáme skrze čas a ne v jejím celku, najednou.
-
Logické programování ještě hůře, proto se neprosadilo skoro vůbec. To jsou vlastně už jen kolekce pravidel nesvázaných pořadím vykonávání. Rozhodnout pak, zda nějaký "jazyk" vstupních dat jim odpovídá, se už jen tak nedá. Pak to, zda program dělá to co má, je založeno už jen na víře.
Je to podobná situace jako v účetnictví, kdy v určitém momentu musíte věřit, že je to dobře zaúčtované, protože zkontrolovat to, znamená udělat ho celé znovu, s dalšími novými chybami.
Ostatně evoluce přijala podobný kompromis, imperativní paradigma, které se projevuje v tom, že realitu vnímáme skrze čas a ne v jejím celku, najednou.
To je princip deklarativních jazyků, že nazáleží na pořadí.
-
FP má nemálo výhod - ale rozhodně to není způsob, jak lidé přirozeně přemýšlí.
Haskell imho nebyl vymyšlen tak, aby se podobal způsobu, jak lidi myslí, ale způsobu, jak myslí matematici ;)
Akorát bych byl trochu opatrnější se ztotožňováním FP s pure default-lazy jazykem. To je jenom podmnožina FP jazyků. A spíš menší: OCaml, Racket, F#, Erlang, Elixir - všechny jsou defaultně eager, čili by se tam normálně napsalo "vem vejce, rozklepni ho, utři ho"
-
FP má nemálo výhod - ale rozhodně to není způsob, jak lidé přirozeně přemýšlí.
Haskell imho nebyl vymyšlen tak, aby se podobal způsobu, jak lidi myslí, ale způsobu, jak myslí matematici ;)
Akorát bych byl trochu opatrnější se ztotožňováním FP s pure default-lazy jazykem. To je jenom podmnožina FP jazyků. A spíš menší: OCaml, Racket, F#, Erlang, Elixir - všechny jsou defaultně eager, čili by se tam normálně napsalo "vem vejce, rozklepni ho, utři ho"
A matematici nejsou lidi? :/
Jinak zrovna matematikům je nějaké programování u ř**i, maximálně to může reflektovat, jak si nějaký programátor myslí, že se matematika dělá. Proto to taky vypadá, jak to vypadá, například ta příšerná syntax. Jazyky navržené matematiky jsou třeba HAL/S či Q systémy, případně i Prolog, ale do toho už kafrali informatici.
-
FP má nemálo výhod - ale rozhodně to není způsob, jak lidé přirozeně přemýšlí.
Haskell imho nebyl vymyšlen tak, aby se podobal způsobu, jak lidi myslí, ale způsobu, jak myslí matematici ;)
Akorát bych byl trochu opatrnější se ztotožňováním FP s pure default-lazy jazykem. To je jenom podmnožina FP jazyků. A spíš menší: OCaml, Racket, F#, Erlang, Elixir - všechny jsou defaultně eager, čili by se tam normálně napsalo "vem vejce, rozklepni ho, utři ho"
main = do
vem_vejce
rozklepni_ho
utři_ho
popř. main = vem_vejce >> rozklepni_ho >> utři_ho
-
main = do
vem_vejce
rozklepni_ho
utři_ho
popř. main = vem_vejce >> rozklepni_ho >> utři_ho
No ano, za tím je ta bižuterie, která je u Haskellu nutně potřeba, aby se dalo psát v sousledných krocích. U jiných jazyků potřeba není. To jsem přece říkal.
-
main = do
vem_vejce
rozklepni_ho
utři_ho
popř. main = vem_vejce >> rozklepni_ho >> utři_ho
No ano, za tím je ta bižuterie, která je u Haskellu nutně potřeba, aby se dalo psát v sousledných krocích. U jiných jazyků potřeba není. To jsem přece říkal.
takže v haskellu to jde stejně jako jinde, ale je to špatně, protože... proč? implementačními detaily se snad zabývat nemusíme, kdo si píše vlastní překladač?
-
takže v haskellu to jde stejně jako jinde, ale je to špatně, protože... proč? implementačními detaily se snad zabývat nemusíme, kdo si píše vlastní překladač?
Nejde to stejně jako jinde. V Haskellu se dá pomocí relativně složitého aparátu (monády) dosáhnout toho chování, které je v jiných FP jazycích defaultní. Ani jedno není vyloženě dobře nebo špatně. Prostě když si zvolím čistotu, tak mi to způsobuje komplikace. Udivuje mě, že je kolem toho tolik řečí, mně to přijde jako zjevná věc.
Jinak zrovna matematikům je nějaké programování u ř**i
Lambda calculus vymyslel matematik :)
-
takže v haskellu to jde stejně jako jinde, ale je to špatně, protože... proč? implementačními detaily se snad zabývat nemusíme, kdo si píše vlastní překladač?
Nejde to stejně jako jinde. V Haskellu se dá pomocí relativně složitého aparátu (monády) dosáhnout toho chování, které je v jiných FP jazycích defaultní.
a to má být výhoda těch jiných jazyků?
-
takže v haskellu to jde stejně jako jinde, ale je to špatně, protože... proč? implementačními detaily se snad zabývat nemusíme, kdo si píše vlastní překladač?
Nejde to stejně jako jinde. V Haskellu se dá pomocí relativně složitého aparátu (monády) dosáhnout toho chování, které je v jiných FP jazycích defaultní. Ani jedno není vyloženě dobře nebo špatně. Prostě když si zvolím čistotu, tak mi to způsobuje komplikace. Udivuje mě, že je kolem toho tolik řečí, mně to přijde jako zjevná věc.
Jinak zrovna matematikům je nějaké programování u ř**i
Lambda calculus vymyslel matematik :)
1) Monády jsou složitý aparát? Dvě funkce a dvě rovnice, která musí splňovat, to mi přijde jako celkem málo.
2) Lambda kalkulus ale není programovací jazyk, right? I do Lispu při vší jeho jednoduchosti naplácali informatici různé ptákoviny (z matematického pohledu).
-
a to má být výhoda těch jiných jazyků?
1. Tohle téma se odpíchlo od Andyho otázky "Já fakt nechápu, co je za komplikace v default-lazy jazyku."
2. Někdo (třeba já) to za výhodu může považovat, protože si může myslet, že programování pomocí sousledných kroků je přirozenější lidskému myšlení a tudíž to chceme jako default a pomocí bižuterie chceme dosahovat lazyness, ne opačně.
Btw, když jsme nakousli to "Proč Haskell vznikl?", koukám na zajímavej paper http://haskell.cs.yale.edu/wp-content/uploads/2011/02/history.pdf a tam je jedna zajímavá informace v podobném duchu:
An anonymous reviewer supplied the following: “An interesting
sidelight is that the Friedman and Wise paper inspired Sussman and
Steele to examine lazy evaluation in Scheme, and for a time they
weighed whether to make the revised version of Scheme call-byname
or call-by-value. They eventually chose to retain the original
call-by-value design, reasoning that it seemed to be much easier to
simulate call-by-name in a call-by-value language (using lambdaexpressions
as thunks) than to simulate call-by-value in a call-byname
language (which requires a separate evaluation-forcing mechanism).
Whatever we might think of that reasoning, we can only
speculate on how different the academic programming-language
landscape might be today had they made the opposite decision.”
Dobrá perlička :)
1) Monády jsou složitý aparát? Dvě funkce a dvě rovnice, která musí splňovat, to mi přijde jako celkem málo.
No jo no, ale vidíš, jak se na tom lidi zasekávaj :)
2) Lambda kalkulus ale není programovací jazyk, right? I do Lispu při vší jeho jednoduchosti naplácali informatici různé ptákoviny (z matematického pohledu).
Ale matematici za to všechno můžou jako prvotní příčina! ;)
-
a to má být výhoda těch jiných jazyků?
1. Tohle téma se odpíchlo od Andyho otázky "Já fakt nechápu, co je za komplikace v default-lazy jazyku."
2. Někdo (třeba já) to za výhodu může považovat, protože si může myslet, že programování pomocí sousledných kroků je přirozenější lidskému myšlení a tudíž to chceme jako default a pomocí bižuterie chceme dosahovat lazyness, ne opačně.
1. lazy a pure jsou jiné koncepty
2. výhoda proti haskellu by to byla, kdyby to v haskellu nešlo nebo šlo složitě, jestli tou bižuterií myslíte IO monádu, tak viz bod 1
-
1. lazy a pure jsou jiné koncepty
Ale úzce spolu souvisí. Znáš nějaký default-lazy (call by name) jazyk, který není pure?
Kromě toho, k tomu, aby ti ty problémy s řazením IO vznikly, je lazyness podmínka dostačující:
a = print "a"
b = print "b"
f(a,b)
- pokud by ten jazyk byl lazy, v jakém pořadí vytiskne "a" a "b"? Na základě čeho?
-
1. lazy a pure jsou jiné koncepty
Ale úzce spolu souvisí. Znáš nějaký default-lazy (call by name) jazyk, který není pure?
Kromě toho, k tomu, aby ti ty problémy s řazením IO vznikly, je lazyness podmínka dostačující:
a = print "a"
b = print "b"
f(a,b)
- pokud by ten jazyk byl lazy, v jakém pořadí vytiskne "a" a "b"? Na základě čeho?
tak hlavně, že programování pomocí sousledných kroků v haskellu není problém
-
1) Monády jsou složitý aparát? Dvě funkce a dvě rovnice, která musí splňovat, to mi přijde jako celkem málo.
No jo no, ale vidíš, jak se na tom lidi zasekávaj :)
2) Lambda kalkulus ale není programovací jazyk, right? I do Lispu při vší jeho jednoduchosti naplácali informatici různé ptákoviny (z matematického pohledu).
Ale matematici za to všechno můžou jako prvotní příčina! ;)
1) Mě by zajímalo, kolik procent lidí monády chápe, resp. kolik lidí je pochopí po přečtení řekněme článku na Wikipedii nebo původního článku z roku 1990. Protože mi připadá, že jde o jednoduchý koncept, ale kde kdo si napíše svůj "ultimate monad tutorial", aniž by monádám rozuměl, čímž zmate další lidi a díky efektu sněhové koule se tento nešvar rozrůstá a zároveň vzniká dojem, že monády jsou něco magického až ezoterického. Přitom stačí příklad na stránku.
2) Jistě, od matematiků to vzešlo, ale spíše bych to formuloval tak, že matematici vymysleli jednoduchou a přímočarou teorii, jíž se poté chopili informatici, "hezky" to všechno zesložitili (lidově řečeno doje*ali) a teď brečí, že se tomu nedá rozumět.
-
1. lazy a pure jsou jiné koncepty
Ale úzce spolu souvisí. Znáš nějaký default-lazy (call by name) jazyk, který není pure?
Kromě toho, k tomu, aby ti ty problémy s řazením IO vznikly, je lazyness podmínka dostačující:
a = print "a"
b = print "b"
f(a,b)
- pokud by ten jazyk byl lazy, v jakém pořadí vytiskne "a" a "b"? Na základě čeho?
Tak purity a laziness (sic!) jsou ortogonální koncepty, a zrovna ten druhý nepovažuju za příliš šťastnou volbu.
-
Tak purity a laziness (sic!) jsou ortogonální koncepty, a zrovna ten druhý nepovažuju za příliš šťastnou volbu.
A jaký smysl by mělo mít jazyk, který by byl čistý ale ne lazy? Pokud je čistý, tak tam funguje referenční transparentnost, takže v čem by ta non-laziness (díky za upozornění na chybu!) měla spočívat?
-
Tak purity a laziness (sic!) jsou ortogonální koncepty, a zrovna ten druhý nepovažuju za příliš šťastnou volbu.
A jaký smysl by mělo mít jazyk, který by byl čistý ale ne lazy? Pokud je čistý, tak tam funguje referenční transparentnost, takže v čem by ta non-laziness (díky za upozornění na chybu!) měla spočívat?
To je právě ono, laziness je v takovém případě irelevantní a vztahuje se jen na implementaci (kde má smysl o ní uvažovat). Proto píšu, že jsou to koncepty ortogonální. Asi jsem se špatně vyjádřil, považuju za poněkud nešťastné o "(non)laziness" vůbec uvažovat, protože při zachování rozumných vlastností (především té již zmíněné čistoty) není smysluplné o ní mluvit.
-
To je právě ono, laziness je v takovém případě irelevantní a vztahuje se jen na implementaci (kde má smysl o ní uvažovat). Proto píšu, že jsou to koncepty ortogonální. Asi jsem se špatně vyjádřil, považuju za poněkud nešťastné o "(non)laziness" vůbec uvažovat, protože při zachování rozumných vlastností (především té již zmíněné čistoty) není smysluplné o ní mluvit.
Právě proto nerozumím tomu, proč jim říkáš ortogonální. Podle mě jsou naopak vzájemně závislé přesně z tohodle důvodu.
Ale to je jedno, nebudem slovíčkařit :) jinak si myslím rozumíme.
-
To je právě ono, laziness je v takovém případě irelevantní a vztahuje se jen na implementaci (kde má smysl o ní uvažovat). Proto píšu, že jsou to koncepty ortogonální. Asi jsem se špatně vyjádřil, považuju za poněkud nešťastné o "(non)laziness" vůbec uvažovat, protože při zachování rozumných vlastností (především té již zmíněné čistoty) není smysluplné o ní mluvit.
Právě proto nerozumím tomu, proč jim říkáš ortogonální. Podle mě jsou naopak vzájemně závislé přesně z tohodle důvodu.
Ale to je jedno, nebudem slovíčkařit :) jinak si myslím rozumíme.
Myslel jsem laziness v implementaci, ne koncepční, ale to už ti asi došlo, takže ano, rozumíme si a dokonce se i shodneme ;)
Možná bychom se po nějaké diskusi shodli i na těch monádách :)
-
Tak purity a laziness (sic!) jsou ortogonální koncepty, a zrovna ten druhý nepovažuju za příliš šťastnou volbu.
A jaký smysl by mělo mít jazyk, který by byl čistý ale ne lazy? Pokud je čistý, tak tam funguje referenční transparentnost, takže v čem by ta non-laziness (díky za upozornění na chybu!) měla spočívat?
take(3, [1..])
take(3, [1,2,3,4,5/0]
def foo = foo
take(3, [1,2,3,4,foo]
Jak (a zda) to skonci zalezi na tom, zda je jazyk liny nebo ne.
-
Tak purity a laziness (sic!) jsou ortogonální koncepty, a zrovna ten druhý nepovažuju za příliš šťastnou volbu.
A jaký smysl by mělo mít jazyk, který by byl čistý ale ne lazy? Pokud je čistý, tak tam funguje referenční transparentnost, takže v čem by ta non-laziness (díky za upozornění na chybu!) měla spočívat?
tak už tady byl zmíněn ten PureScript
-
Jak (a zda) to skonci zalezi na tom, zda je jazyk liny nebo ne.
Ty výrazy mají přesně definovanou hodnotu a jakým způsobem se na ni převedou (jestli korektně nebo nekorektně) je jenom o implementaci, ne o jazyku. To je to, co říkal zboj.
Pokud by se program na těch výrazech zasekl, tak ten jazyk nesplňuje referenční transparentnost, což je definiční znak pure jazyka.
-
tak už tady byl zmíněn ten PureScript
Jenže tam to je z toho implementačního (meta) důvodu, který byl taky zmíněn - transpilace do čitelného JS.
-
Ty výrazy mají přesně definovanou hodnotu a jakým způsobem se na ni převedou (jestli korektně nebo nekorektně) je jenom o implementaci, ne o jazyku. To je to, co říkal zboj.
Pokud by se program na těch výrazech zasekl, tak nesplňuje referenční transparentnost, což je definiční znak pure jazyka.
Ne.
-
Ne.
Co ne? Není venku slunečno? Souhlasím :)
-
Jak (a zda) to skonci zalezi na tom, zda je jazyk liny nebo ne.
Ty výrazy mají přesně definovanou hodnotu a jakým způsobem se na ni převedou (jestli korektně nebo nekorektně) je jenom o implementaci, ne o jazyku. To je to, co říkal zboj.
Pokud by se program na těch výrazech zasekl, tak ten jazyk nesplňuje referenční transparentnost, což je definiční znak pure jazyka.
:D
-
Ne.
Co ne? Není venku slunečno? Souhlasím :)
take(3,[1..])
ma jinou hodnotu podle toho, jaky mas jazyk. Muze to byt [1,2,3] nebo ⊥
-
take(3,[1..])
ma jinou hodnotu podle toho, jaky mas jazyk. Muze to byt [1,2,3] nebo ⊥
Ok, beru. Jistě můžu mít jazyk s libovolně nesmyslnou sémantikou. Třeba takovou, že 1+1 = ⊥ nebo take(3,[1..]) = ⊥. Takže formálně máš pravdu, uznávám. Bavíme se ale o tom, jestli by takový jazyk mělo smysl vytvářet. Já říkám, že si nedovedu moc představit, k čemu by bylo dobré mít pure jazyk bez línosti.
Jaký význam má pojem "nelínost" u pure jazyka? Že se občas zacyklí na něčem, na čem by se zacyklit nemusel? Vlastnost líný/nelíný prostě zásadně ovlivňuje chování u ne-pure jazyků, ale u pure jazyků nemá na nic vliv (kromě tohodle případu, kde je jenom na škodu).
U ne-pure jazyků totiž výraz musím vyhodnotit pokaždé, kdy mě zajímá jeho hodnota. U pure jazyka mi stačí ho vyhodnotit jednou. Proto je mi celkem jedno, kdy to udělám. V principu bych tu tvoji sémantiku mohl korektně naplnit i tak, že si předem zjistím, že se program nutně dostane k vyhodnocování take(3,[1..]), takže se na celý program vykašlu a hned spustím "for( ; ; ) ;"
-
take(3,[1..])
ma jinou hodnotu podle toho, jaky mas jazyk. Muze to byt [1,2,3] nebo ⊥
Ok, beru. Jistě můžu mít jazyk s libovolně nesmyslnou sémantikou.
To je naprosto normalni semantika striktniho jazyka.
Jaký význam má pojem "nelínost" u pure jazyka? Že se občas zacyklí na něčem, na čem by se zacyklit nemusel?
Spotreba pameti. Dokud nedojde na ⊥, tak se u striktnich jazyku lepe resi pametova narocnost.
-
Zde máte příklad z Haskellu: (bohužel na GitHubu jsem nenarazil na reálnou aplikaci, jen školometské příklady)
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' _ [] _ = []
zipWith' _ _ [] = []
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
Při jeho analýze setrváváte neustále ve fonologické smyčce. Detailněji viz zde https://cs.wikipedia.org/wiki/Baddeleyho_model_pracovn%C3%AD_pam%C4%9Bti
Co na tom příkladu není jasné? Jaká smyčka????
-
take(3,[1..])
ma jinou hodnotu podle toho, jaky mas jazyk. Muze to byt [1,2,3] nebo ⊥
Ok, beru. Jistě můžu mít jazyk s libovolně nesmyslnou sémantikou. Třeba takovou, že 1+1 = ⊥ nebo take(3,[1..]) = ⊥. Takže formálně máš pravdu, uznávám. Bavíme se ale o tom, jestli by takový jazyk mělo smysl vytvářet. Já říkám, že si nedovedu moc představit, k čemu by bylo dobré mít pure jazyk bez línosti.
Jaký význam má pojem "nelínost" u pure jazyka? Že se občas zacyklí na něčem, na čem by se zacyklit nemusel? Vlastnost líný/nelíný prostě zásadně ovlivňuje chování u ne-pure jazyků, ale u pure jazyků nemá na nic vliv (kromě tohodle případu, kde je jenom na škodu).
U ne-pure jazyků totiž výraz musím vyhodnotit pokaždé, kdy mě zajímá jeho hodnota. U pure jazyka mi stačí ho vyhodnotit jednou. Proto je mi celkem jedno, kdy to udělám. V principu bych tu tvoji sémantiku mohl korektně naplnit i tak, že si předem zjistím, že se program nutně dostane k vyhodnocování take(3,[1..]), takže se na celý program vykašlu a hned spustím "for( ; ; ) ;"
Různé sémantiky pro symbolický systém jsou probémem obecně a nejen v CS, viz např. formální logika a různé sémantiky pro různé dedukční systémy nebo teorie množin a její různé modely (např. ten Gödelův). V IT je to jen názornější, protože na rozdíl od matematických teorií se programy výpočetně vyhodnocují, takže si člověk - když napíše něco blbě nebo si není vědom sématického problému - rychle nabije čumák. Nejhezčí příklad, o němž vím, je v Prologu SLD rezoluce vs. SLG, v té druhé se žádný program nikdy nezacyklí, kdežto v té první je problémem jakákoliv levá rekurze, např. něco jako borders(X, Y) :- borders(Y, X). Čili nemá moc smysl se tu o tom dále hádat, neboť problém je trochu jinde.
-
(Aby bylo jasno, ja mam linost na haskellu rad. Ale i kdyz se clovek jeste pohybuje na ciste FP uzemi, tak tam rozdily jsou a ne ciste jenom ve prospech lenosti.)
-
Co na tom příkladu není jasné? Jaká smyčka????
Ivan s tim ma problem a tak si mysli, ze je to problem ;)
-
To je naprosto normalni semantika striktniho jazyka.
Kind of. Céčko třeba žádné nekonečné listy nemá, čili to jeho sémantika není. A ano, je nesmyslná. Protože první tři prvky z nekonečného listu jsou dobře definované první tři prvky. Není to nedefinovaná hodnota jako třeba 1/0.
Čili pokud budeš vědět, že k tomuhle problému by u tvého jazyka mohlo dojít, tak tam vůbec nekonečné listy nedáš, nebo je tam dáš v symbolické formě, u které k zacyklení dojít nemůže (tak to má např. Elixir, který je eager, nekonečné listy má a nezacykluje se na nich).
Spotreba pameti. Dokud nedojde na ⊥, tak se u striktnich jazyku lepe resi pametova narocnost.
To už ale mluvíš o implementaci, ne o jazyku.
-
Jaký význam má pojem "nelínost" u pure jazyka? Že se občas zacyklí na něčem, na čem by se zacyklit nemusel? Vlastnost líný/nelíný prostě zásadně ovlivňuje chování u ne-pure jazyků, ale u pure jazyků nemá na nic vliv (kromě tohodle případu, kde je jenom na škodu).
Teď jsem se nějak zamotal.... ale Haskell je líný v té pure části, efektové části musí být v monadu a ten se líně nevyhodnocuje... tak v čem je teda problém?
Jinak co třeba tuhle úžasnost, jejíž varianta se např. používá ve FRP:
forkService :: (Service -> IO ()) -> IO Service
forkService svcCode = mdo
let service = Service "New service" threadid
theadid <- forkIO (svcCode service)
return service
Tohle chce trochu lenosti, a přesto víš, co se kdy přesně vyhodnotí.
-
efektové části musí být v monadu a ten se líně nevyhodnocuje...
Jaktože ne?
-
To je naprosto normalni semantika striktniho jazyka.
Kind of. Céčko třeba žádné nekonečné listy nemá, čili to jeho sémantika není.
Ale, ale.
int foo() { for (;;); }
int bar(a, b) { return a; }
bar(1,foo());
a zase mas ⊥ (jenom tomu proste Ceckar tak nerika, protoze zije v jinem svete)
-
Ale, ale.
int foo() { for (;;); }
int bar(a, b) { return a; }
bar(1,foo());
a zase mas ⊥ (jenom tomu proste Ceckar tak nerika, protoze zije v jinem svete)
To je ale úplně jiný případ. Tím jsi jenom ilustroval, že se v céčku dá napsat kód, který se zacyklí.
-
efektové části musí být v monadu a ten se líně nevyhodnocuje...
Jaktože ne?
Tak teď nevím, jestli se ptáš na tu úžasnou teorii nebo na praxi pod tím, ale standardně neexistuje možnost napsat něco typu, co jsi napsal:
let a = print "hello"
let b = print "world"
f a b
Resp. to nedělá to, že by to nedeterministicky vyhodnotilo "a" a "b". Takže když se bavím o laziness v případě efektů, tak ta v haskellu prostě neexistuje (vyjma unsafePerformIO, ale to je něco, jako když v jakémkoliv jiném jazyce odskočíš do assembleru). Dobře, můžeš tvrdit, že IO Monad se vyhodnocuje nějak "líně" tím, že se tam protahuje přes všechny funkce RealWord#, ale to je prostě nějaké teoretické zdůvodnění - prakticky se to prostě vyhodnocuje "eager" (teda mě není vůbec moc jasné, co vlastně v případě monadu má znamenat "lazy" a "strict"....)
-
Ale, ale.
int foo() { for (;;); }
int bar(a, b) { return a; }
bar(1,foo());
a zase mas ⊥ (jenom tomu proste Ceckar tak nerika, protoze zije v jinem svete)
To je ale úplně jiný případ. Tím jsi jenom ilustroval, že se v céčku dá napsat kód, který se zacyklí.
To je furt ten samy pripad. (pokud mas problem s tim [1..], tak si vezmi ten priklad s
def foo = foo
take(3, [1,2,3,4,foo])
ten ti taky 'konci' bud [1,2,3] nebo ⊥)
Ve striktnim jazyce cokoli v podobe baz(..., ⊥, ...) je ⊥. V non-strict muze a nemusi.
-
Dobře, můžeš tvrdit, že IO Monad se vyhodnocuje nějak "líně" tím, že se tam protahuje přes všechny funkce RealWord#, ale to je prostě nějaké teoretické zdůvodnění - prakticky se to prostě vyhodnocuje "eager" (teda mě není vůbec moc jasné, co vlastně v případě monadu má znamenat "lazy" a "strict"....)
To není teoretické zdůvodnění, to je princip, jakým to funguje.
Mohlo by to být i jinak, třeba že bys generoval list IO operací a ty potom realtime provedl přesně v tom pořadí, v jakém v listu jsou. Ale to by bylo mimo jazyk. Ten jazyk sám o sobě by zůstal pořád líný.
Prostě pokud v líném jazyce chceš vynutit pořadí, uděláš to třeba pomocí monády a pokud chceš vynutit línost v nelíném jazyce, uděláš to třeba pomocí futures. Oboje jde a ani jedno neporušuje línost/nelínost toho jazyka. Je to jenom způsob, jak ho nějakou fintou donutit udělat něco, co by jinak "sám od sebe" negarantoval.
...no a to je právě ta pointa: mně přijde, že pořadí potřebujeme častěji, proto je lepší ho mít jako default a finty používat na línost, ne naopak.
-
Ve striktnim jazyce cokoli v podobe baz(..., ⊥, ...) je ⊥. V non-strict muze a nemusi.
Souhlas. A plyne z toho co?
-
Ve striktnim jazyce cokoli v podobe baz(..., ⊥, ...) je ⊥. V non-strict muze a nemusi.
Souhlas. A plyne z toho co?
Teoreticky - lazy je u pure FP "lepsi", protoze toho spocita vic.
Prakticky - nic moc, protoze zase zaplatis praktickou cenu v realne implementaci.
-
Teoreticky - lazy je u pure FP "lepsi", protoze toho spocita vic.
Prakticky - nic moc, protoze zase zaplatis praktickou cenu v realne implementaci.
Čili jsme se dostali k tomu, co jsem říkal? Že si moc nedovedu představit, k čemu by bylo dobré mít non-lazy pure jazyk? ;)
-
Teoreticky - lazy je u pure FP "lepsi", protoze toho spocita vic.
Prakticky - nic moc, protoze zase zaplatis praktickou cenu v realne implementaci.
Čili jsme se dostali k tomu, co jsem říkal? Že si moc nedovedu představit, k čemu by bylo dobré mít non-lazy pure jazyk? ;)
mi přijde, že vůbec nechápete, k čemu to "pure" je
-
mi přijde, že vůbec nechápete, k čemu to "pure" je
Tak mi to zkus vysvětlit. Řekni, "k čemu to pure je" a kde je to v rozporu s něčím, co jsem řekl.
-
Mohlo by to být i jinak, třeba že bys generoval list IO operací a ty potom realtime provedl přesně v tom pořadí, v jakém v listu jsou. Ale to by bylo mimo jazyk. Ten jazyk sám o sobě by zůstal pořád líný.
Ale mě čím dál víc připadá, že to tak v podstatě je - že IO je jen způsob jak v "pure" jazyce popsat, jak se ty efekty mají dělat a nějaký "interpret" to následně provede. Ostatně dnes je docela časté si napsat vlastní interpret (Free/Operational monady) a psát si pak kód ve vlastním monadu.
Prostě pokud v líném jazyce chceš vynutit pořadí, uděláš to třeba pomocí monády a pokud chceš vynutit línost v nelíném jazyce, uděláš to třeba pomocí futures. Oboje jde a ani jedno neporušuje línost/nelínost toho jazyka. Je to jenom způsob, jak ho nějakou fintou donutit udělat něco, co by jinak "sám od sebe" negarantoval.
A není to nakonec Monad docela hezký způsob jak popsat, co má program dělat? Jak bys to popsal jinak? Monad je způsob, jak něco popíšeš, je to datová struktura, se kterou jsi schopen nějak pracovat a vyhodnocovat ji. V ostatních jazycích mi připadá, že funkce je de-fakto "black-box". Monad ten black-box vlastně umožní rozložit. Ale když se ti to nelíbí, tak vůbec nemusíš řešit, jak to funguje - (IO x) ber jako "pointer na funkci", a když to vložíš do těla IO funkce, tak se vykoná. A jsi zpátky v normálních jazycích, v čem je problém?
...no a to je právě ta pointa: mně přijde, že pořadí potřebujeme častěji, proto je lepší ho mít jako default a finty používat na línost, ne naopak.
Mě naopak připadá, že dát tam nějaký hack kvůli tomu, že chceš IO popsat nějak jinak, než všechno ostatní, co děláš, by nebylo úplně dobré....
-
Prostě pokud v líném jazyce chceš vynutit pořadí, uděláš to třeba pomocí monády a pokud chceš vynutit línost v nelíném jazyce, uděláš to třeba pomocí futures. Oboje jde a ani jedno neporušuje línost/nelínost toho jazyka. Je to jenom způsob, jak ho nějakou fintou donutit udělat něco, co by jinak "sám od sebe" negarantoval.
A není to nakonec Monad docela hezký způsob jak popsat, co má program dělat? Jak bys to popsal jinak? Monad je způsob, jak něco popíšeš, je to datová struktura, se kterou jsi schopen nějak pracovat a vyhodnocovat ji. V ostatních jazycích mi připadá, že funkce je de-fakto "black-box". Monad ten black-box vlastně umožní rozložit. Ale když se ti to nelíbí, tak vůbec nemusíš řešit, jak to funguje - (IO x) ber jako "pointer na funkci", a když to vložíš do těla IO funkce, tak se vykoná. A jsi zpátky v normálních jazycích, v čem je problém?
Přesně o tom monády jsou a upřímně fakt nevím, proč o nich někdo pochybuje.
-
mi přijde, že vůbec nechápete, k čemu to "pure" je
Tak mi to zkus vysvětlit. Řekni, "k čemu to pure je" a kde je to v rozporu s něčím, co jsem řekl.
on to tu někdo hezky ilustroval při diskuzi o dokumentační hodnotě typových signatur, jednoduše řečeno, jakou informační hodnotu by měla signatura např. Num a => a -> a pokud by jazyk nebyl pure?
-
Mě naopak připadá, že dát tam nějaký hack kvůli tomu, že chceš IO popsat nějak jinak, než všechno ostatní, co děláš, by nebylo úplně dobré....
proč si něco nalhávat? monády v haskellu jsou hack kvůli potřebě popsat IO a udržet zbytek čistý
-
Mě naopak připadá, že dát tam nějaký hack kvůli tomu, že chceš IO popsat nějak jinak, než všechno ostatní, co děláš, by nebylo úplně dobré....
proč si něco nalhávat? monády v haskellu jsou hack kvůli potřebě popsat IO a udržet zbytek čistý
To není tak úplně pravda. Monády jsou "čisté" ve smyslu FP. Navíc tam nejsou jen pro IO. Chce to ale ponořit se trochu hlouběji do matematického aparátu, jenž za nimi stojí.
-
Tak mi to zkus vysvětlit. Řekni, "k čemu to pure je" a kde je to v rozporu s něčím, co jsem řekl.
on to tu někdo hezky ilustroval při diskuzi o dokumentační hodnotě typových signatur, jednoduše řečeno, jakou informační hodnotu by měla signatura např. Num a => a -> a pokud by jazyk nebyl pure?
A to má být v rozporu s něčím, co jsem řekl? Proč údajně konceptu "pure" nerozumím?
-
A není to nakonec Monad docela hezký způsob jak popsat, co má program dělat?
Je to způsob, jak zabezpečit pořadí vyhodnocování v jazyce, který ho jinak negarantuje. Konceptuálně je to na úrovni toho listu operací, akorát Monad je mazanější :)
Jak bys to popsal jinak?
V tomhle není sporu. Mně se jenom nelíbilo, jak's psal o tom, že jakoby v monádách laziness neplatí. Platí úplně stejně jako kdekoli jinde. Je to právě způsob, jak i přes laziness dosáhnout řazení.
-
To není tak úplně pravda.
Není to ani trochu pravda.
-
Mě naopak připadá, že dát tam nějaký hack kvůli tomu, že chceš IO popsat nějak jinak, než všechno ostatní, co děláš, by nebylo úplně dobré....
proč si něco nalhávat? monády v haskellu jsou hack kvůli potřebě popsat IO a udržet zbytek čistý
To není tak úplně pravda. Monády jsou "čisté" ve smyslu FP. Navíc tam nejsou jen pro IO. Chce to ale ponořit se trochu hlouběji do matematického aparátu, jenž za nimi stojí.
hack je možná trochu silné slovo, ani netvrdím, že nejsou užitečná i jinde, sám je používám až příliš, nicméně viz "tackling the awkward squad" :)
-
A není to nakonec Monad docela hezký způsob jak popsat, co má program dělat?
Je to způsob, jak zabezpečit pořadí vyhodnocování v jazyce, který ho jinak negarantuje. Konceptuálně je to na úrovni toho listu operací, akorát Monad je mazanější :)
Jak bys to popsal jinak?
V tomhle není sporu. Mně se jenom nelíbilo, jak's psal o tom, že jakoby v monádách laziness neplatí. Platí úplně stejně jako kdekoli jinde. Je to právě způsob, jak i přes laziness dosáhnout řazení.
Souhlas. A teď jen nechápu, proč je teda laziness v haskellu problém...
-
hack je možná trochu silné slovo, ani netvrdím, že nejsou užitečná i jinde, sám je používám až příliš, nicméně viz "tackling the awkward squad" :)
Tam je právě úplně explicitně napsán opak toho, co tvrdíš: "Well, if the side effect can’t be in the functional program, it will have to be outside it" (str. 3)
Žádný "hack", žádné zatahování nečistých věcí. Pořád ta stejná čistota - čistým způsobem se generují instrukce k provádění nečistých věcí (vně jazyka samotného).
-
To není tak úplně pravda.
Není to ani trochu pravda.
Jen jsem to napsal trochu diplomatičtěji ;)
-
Souhlas. A teď jen nechápu, proč je teda laziness v haskellu problém...
Není to problém. Je to omezení, které způsobuje, že jednoduché věci musíš pak dělat složitě (pomocí té IO monády). Prostě je to trade off - je to krásná vlastnost, ale něco stojí (a stojí hodně). Proto většina FP jazyků jde opačnou cestou - jsou "nečisté", ale díky tomu snadněji použitelné.
-
Proto většina FP jazyků jde opačnou cestou - jsou "nečisté", ale díky tomu snadněji použitelné.
Resp. ne-čisté a ne-lazy, ať se zas netaháme za nohu :)
-
Mě naopak připadá, že dát tam nějaký hack kvůli tomu, že chceš IO popsat nějak jinak, než všechno ostatní, co děláš, by nebylo úplně dobré....
proč si něco nalhávat? monády v haskellu jsou hack kvůli potřebě popsat IO a udržet zbytek čistý
To není tak úplně pravda. Monády jsou "čisté" ve smyslu FP. Navíc tam nejsou jen pro IO. Chce to ale ponořit se trochu hlouběji do matematického aparátu, jenž za nimi stojí.
hack je možná trochu silné slovo, ani netvrdím, že nejsou užitečná i jinde, sám je používám až příliš, nicméně viz "tackling the awkward squad" :)
Já bych je spíš popsal jako "sofistikovaný způsob dosažení cíle bez narušení a priori daných self-imposed principů". Ale to už je dost subjektivní hodnocení. Nicméně formálně opravdu "čisté" (ve smyslu FP) jsou.
-
Já bych je spíš popsal jako "sofistikovaný způsob dosažení cíle bez narušení a priori daných self-imposed principů".
není možné, že jste opět diplomatický?
Nicméně formálně opravdu "čisté" (ve smyslu FP) jsou.
osobně s čistotou IO v haskellu nemám žádný filosofický ani jiný problém, ale najdou se tací, kteří mají, i tady na rootu se o tom vedly dabaty
-
Já bych je spíš popsal jako "sofistikovaný způsob dosažení cíle bez narušení a priori daných self-imposed principů".
není možné, že jste opět diplomatický?
Nicméně formálně opravdu "čisté" (ve smyslu FP) jsou.
osobně s čistotou IO v haskellu nemám žádný filosofický ani jiný problém, ale najdou se tací, kteří mají, i tady na rootu se o tom vedly dabaty
Jo, je to možné :) Jinak mi je čistota Haskellu úplně ukradená, jen se mi prostě líbí použití dost abstraktního konceptu (monády) na něco praktického, nicméně přiznávám, že v tomto případě mně zajímá hlavně ta formální (matematická) stránka věci, protože funkcionálně píšu jen v jazycích převážně imperativních. Konkrétně u Haskellu mi ta debata navíc přijde dost žabomyší.
-
Souhlas. A teď jen nechápu, proč je teda laziness v haskellu problém...
Není to problém. Je to omezení, které způsobuje, že jednoduché věci musíš pak dělat složitě (pomocí té IO monády). Prostě je to trade off - je to krásná vlastnost, ale něco stojí (a stojí hodně). Proto většina FP jazyků jde opačnou cestou - jsou "nečisté", ale díky tomu snadněji použitelné.
tvrdíte, že kdyby byl haskell "strict", nepotřeboval by IO monádu?
-
tvrdíte, že kdyby byl haskell "strict", nepotřeboval by IO monádu?
To byste musel definovat, co myslíte tím "strict" v kontextu pure jazyka. Já pořád moc nevím, co si pod tím mám představit.
Kdyby nebyl pure, tak IO monádu nepotřebuje, o tom snad není sporu. Takových jazyků je několik.
-
P.S. a omlouvám se, teď se na chvíli debaty nebudu účastnit, dám nezdvořile přednost paření Wolfa ;)
-
tvrdíte, že kdyby byl haskell "strict", nepotřeboval by IO monádu?
To byste musel definovat, co myslíte tím "strict" v kontextu pure jazyka. Já pořád moc nevím, co si pod tím mám představit.
Kdyby nebyl pure, tak IO monádu nepotřebuje, o tom snad není sporu. Takových jazyků je několik.
strict: před vyhodnocením funkce jsou vyhodnoceny její argumenty
takže jak už víceméně zaznělo:
f a b = a
strict: f 1 undefined = undefined
lazy: f 1 undefined = 1
(snad to tak stačí, nedělám si nárok na matematickou preciznost)
-
Já bych je spíš popsal jako "sofistikovaný způsob dosažení cíle bez narušení a priori daných self-imposed principů".
není možné, že jste opět diplomatický?
Nicméně formálně opravdu "čisté" (ve smyslu FP) jsou.
osobně s čistotou IO v haskellu nemám žádný filosofický ani jiný problém, ale najdou se tací, kteří mají, i tady na rootu se o tom vedly dabaty
Jo, je to možné :) Jinak mi je čistota Haskellu úplně ukradená, jen se mi prostě líbí použití dost abstraktního konceptu (monády) na něco praktického, nicméně přiznávám, že v tomto případě mně zajímá hlavně ta formální (matematická) stránka věci, protože funkcionálně píšu jen v jazycích převážně imperativních. Konkrétně u Haskellu mi ta debata navíc přijde dost žabomyší.
aspoň mám důvod či záminku dostudovat trochu teorie, zároveň si říkám, že takové debaty se asi vedly o kvantové fyzice nebo kvazikrystalech :)
-
tvrdíte, že kdyby byl haskell "strict", nepotřeboval by IO monádu?
To byste musel definovat, co myslíte tím "strict" v kontextu pure jazyka. Já pořád moc nevím, co si pod tím mám představit.
Mel jsi tu par prikladu, kdy se ti neco 'vyhodnoti' bud na ⊥ nebo jinou hodnotu podle toho zda predavas parametry striktne nebo line. Ty priklady se chovaji stejne v pure i nepure jazycich.
Edit: v mne, koukam, predbehl ;)
-
Já bych je spíš popsal jako "sofistikovaný způsob dosažení cíle bez narušení a priori daných self-imposed principů".
není možné, že jste opět diplomatický?
Nicméně formálně opravdu "čisté" (ve smyslu FP) jsou.
osobně s čistotou IO v haskellu nemám žádný filosofický ani jiný problém, ale najdou se tací, kteří mají, i tady na rootu se o tom vedly dabaty
Jo, je to možné :) Jinak mi je čistota Haskellu úplně ukradená, jen se mi prostě líbí použití dost abstraktního konceptu (monády) na něco praktického, nicméně přiznávám, že v tomto případě mně zajímá hlavně ta formální (matematická) stránka věci, protože funkcionálně píšu jen v jazycích převážně imperativních. Konkrétně u Haskellu mi ta debata navíc přijde dost žabomyší.
aspoň mám důvod či záminku dostudovat trochu teorie, zároveň si říkám, že takové debaty se asi vedly o kvantové fyzice nebo kvazikrystalech :)
Jo, ono je občas užitečné se ponořit do teorie a v klidu si nějaké abstraktnější věci promyslet. Pro volné chvíle doporučuji kryptomonády :)
-
Souhlas. A teď jen nechápu, proč je teda laziness v haskellu problém...
Není to problém. Je to omezení, které způsobuje, že jednoduché věci musíš pak dělat složitě (pomocí té IO monády). Prostě je to trade off - je to krásná vlastnost, ale něco stojí (a stojí hodně). Proto většina FP jazyků jde opačnou cestou - jsou "nečisté", ale díky tomu snadněji použitelné.
Tobě "do" syntaxe připadá složitá? Jediný, co potřebuješ pochopit, je rozdíl mezi pure a non-pure funkcí. A pak se s tím programuje úplně stejně jako v jiném jazyce.
Jinak z praxy - naprosto běžně se mi stává, že něco napíšu v IO, pak tam dodám State, sem tam i nějaký Reader, občas to pak celý přesunu do úplně jiného monadu. Takhle je to docela pohodlné, představ, že bych zrovna IO měl nějak jinak mě dost děsí...
-
strict: před vyhodnocením funkce jsou vyhodnoceny její argumenty
V tom případě by IO monádu nepotřeboval, protože řazení IO by pak šlo triviálně udělat takhle:
f(print "b",g(print "a"))
- dle té definice by pořadí výpisu nutně bylo a,b. Funkce f a g můžou dělat cokoli, může to být klidně identita.
-
Tobě "do" syntaxe připadá složitá? Jediný, co potřebuješ pochopit, je rozdíl mezi pure a non-pure funkcí.
Žádné non-pure fce ale neexistují :)
-
P.S. a omlouvám se, teď se na chvíli debaty nebudu účastnit, dám nezdvořile přednost paření Wolfa ;)
P.P.S. Dohráno na úroveň Ueber! Wow wow wow! :)))
-
V tom případě by IO monádu nepotřeboval, protože řazení IO by pak šlo triviálně udělat takhle:
Ona ale ta otázka je stejně bezpředmětná, protože IO monádu nepotřebuje ani když je lazy. Těch způsobů, jak ředit IO operace, je prostě víc.
-
V tom případě by IO monádu nepotřeboval, protože řazení IO by pak šlo triviálně udělat takhle:
f(print "b",g(print "a"))
- dle té definice by pořadí výpisu nutně bylo a,b. Funkce f a g můžou dělat cokoli, může to být klidně identita.
Tak to zase ne.
Ze je jazyk striktni jeste neznamena, ze je urcene poradi vyhodnocovani parametru funkce! Pouze to, ze jsou vsechny vyhodnocene jeste driv, nez se pokracuje tou funkci.
(Jinak to, co ukazujes, uz samozrejme neni pure.)
-
V tom případě by IO monádu nepotřeboval, protože řazení IO by pak šlo triviálně udělat takhle:
f(print "b",g(print "a"))
- dle té definice by pořadí výpisu nutně bylo a,b. Funkce f a g můžou dělat cokoli, může to být klidně identita.
Tak to zase ne.
Ze je jazyk striktni jeste neznamena, ze je urcene poradi vyhodnocovani parametru funkce! Pouze to, ze jsou vsechny vyhodnocene jeste driv, nez se pokracuje tou funkci.
(Jinak to, co ukazujes, uz samozrejme neni pure.)
tak
-
Jinak mohlo by se hodit:
https://wiki.haskell.org/What_a_Monad_is_not
-
Ze je jazyk striktni jeste neznamena, ze je urcene poradi vyhodnocovani parametru funkce! Pouze to, ze jsou vsechny vyhodnocene jeste driv, nez se pokracuje tou funkci.
No - a tady se právě dostáváme do toho, že v pure jazyce prostě imho těžko nadefinuješ call-by-value nějak smysluplně. Můžu ten příklad klíďopíďo přepsat takhle:
let
x = f (print "a")
in
g (print "b") x
- v tomhle případě taky není pořadí definováno?
(Jinak to, co ukazujes, uz samozrejme neni pure.)
A jak to poznáš bez signatury? print může být klidně pure funkce vracející IO akci.
-
Mimochodem, nějak ztrácím přehled, co se vlastně snažíte dokázat. Možná by nebylo od věci to jasně formulovat místo snahy chytit mě na švestkách :)
-
Jinak mohlo by se hodit:
https://wiki.haskell.org/What_a_Monad_is_not
Spíš myslím, že by se mohlo hodit triviální konstatování: nemají-li fce vedlejší efekty => žádným způsobem nemůžou ovlivňovat okolní svět => je úplně jedno, v jakém pořadí se vyhodnocují - jediný důsledek, který to může mít, je zacyklení se nebo nezacyklení => na IO to nemá žádný vliv
-
No - a tady se právě dostáváme do toho, že v pure jazyce prostě imho těžko nadefinuješ call-by-value nějak smysluplně.
Vzdyt jsi tu mel priklady...
Můžu ten příklad klíďopíďo přepsat takhle:
let
x = f (print "a")
in
g (print "b") x
- v tomhle případě taky není pořadí definováno?
Presne tak.
(Jinak to, co ukazujes, uz samozrejme neni pure.)
A jak to poznáš bez signatury? print může být klidně pure funkce vracející IO akci.
Tak pokud je to jenom pure funkce vracejici IO akci, tak zase nemas poradi, i kdyby bylo definovane poradi vyhodnocovani parmetru...
-
Spíš myslím, že by se mohlo hodit triviální konstatování: nemají-li fce vedlejší efekty => žádným způsobem nemůžou ovlivňovat okolní svět => je úplně jedno, v jakém pořadí se vyhodnocují - jediný důsledek, který to může mít, je zacyklení se nebo nezacyklení
Tak to pro zmenu ne...
Muzes si v tech prikadech nahradit nekonecne cykly runtime chybou...
Jinak to posledni
=> na IO to nemá žádný vliv
bys snad mohl nahlednout bez cele te serie sipek, ktera je navic spatne?
-
Mimochodem, nějak ztrácím přehled, co se vlastně snažíte dokázat. Možná by nebylo od věci to jasně formulovat místo snahy chytit mě na švestkách :)
Ze i v ciste pure svete ma teoreticke i prakticke (to jsou ty, kvuli kterym se nepouziva jenom lepsi lazy) dusledky (ne)striktnost
-
Tak pokud je to jenom pure funkce vracejici IO akci, tak zase nemas poradi, i kdyby bylo definovane poradi vyhodnocovani parmetru...
Jasně, beru zpět, celý to byla zcestná argumentace. Mohl bych si sice vynutit pořadí vyhodnocení, ale nic bych z toho neměl, protože to vyhodnocení stejně nemůže dělat IO.
Prostě na nutnosti dělání opiček kolem IO by se nic nezměnilo při jakémkoliv způsobu vyhodnocování.
Ze i v ciste pure svete ma teoreticke i prakticke (to jsou ty, kvuli kterym se nepouziva jenom lepsi lazy) dusledky (ne)striktnost
O tom přece není sporu. Ujasnili jsme si, že to má vliv na možnost zacyklení se na něčem, na čem by se výpočet zacyklit neměl. Ještě na něco jiného?
-
Ze i v ciste pure svete ma teoreticke i prakticke (to jsou ty, kvuli kterym se nepouziva jenom lepsi lazy) dusledky (ne)striktnost
O tom přece není sporu. Ujasnili jsme si, že to má vliv na možnost zacyklení se na něčem, na čem by se výpočet zacyklit neměl. Ještě na něco jiného?
Minimalne jeste na runtime chyby (trebas nepovedeny pattern match). Mozna jeste neco, co ted nevidim.
(Z tech praktickych veci pak samozrejme vliv na spotrebu pameti a rychlost)
-
Minimalne jeste na runtime chyby (trebas nepovedeny pattern match). Mozna jeste neco, co ted nevidim.
(Z tech praktickych veci pak samozrejme vliv na spotrebu pameti a rychlost)
A co?
Můžu ten příklad klíďopíďo přepsat takhle:
let
x = f (print "a")
in
g (print "b") x
- v tomhle případě taky není pořadí definováno?
Presne tak.
Takže hodnota x před vyhodnocením g je teda jaká? Žádná, protože f zatím nebylo potřeba vyhodnotit? A tomu pořád říkáme "strict"?
-
Tobě "do" syntaxe připadá složitá? Jediný, co potřebuješ pochopit, je rozdíl mezi pure a non-pure funkcí.
Žádné non-pure fce ale neexistují :)
No jasně, tak rozdíl mezi pure funkcí a IO akcí. Ale opakuju otázku - co je na IO monádu složitého? Ten monád je doslova (jak jsi psal) to, že pouští jeden "příkaz" za druhým. Žádné výhybky tam nejsou, žádné nedeterministické větvení, nic. Co na tom je složitého? Je to středník, který je naprogramovaný jako středník, takže "do" se chová tak, jak by člověk ve většině programovacích jazyků čekal ("return" na první pohled nevypadá jako šťastná volba, ale po několika kapitolách pure programování mě teda ani nenapadlo, že by to snad tu funkci mělo ukončit - to jsem fakt spíš bojoval s tím, kdy použít "let" a kdy "<-").
Taky se v tom začínám ztrácet, ale tak nějak ty tady v zásadě pořád říkáš, že laziness v zásadě způsobuje složitost haskellu a následně se takové věci jako řazení IO musí řešit přes monády. Já bych asi i souhlasil s tím, že to byl historický vývoj, ale dostali jsme se někam, kde se ukazuje, že to sice na začátku jako opičárna vypadala, ale vlastně to byl docela dobrý nápad.
- v pure výpočtech je laziness celkem putna, mnohdy je spíš lepší
- haskell je v zásadě celý pure včetně IO
- podle tebe je celé IO opičárna, ale mě nějak vůbec není jasné, co by mělo být lepší. Přestat si v IO hrát na pure programování, a pak když se rozhodneš, že místo IO použiješ nějaký Free, nebo to obalíš RWS, tak ten refaktoring bude znamenat přepisování do nějaké jiné syntaxe? Nebo že ti překladač bude automaticky detekovat, jestli je funkce v IO nebo ne a v nějaké syntaxi typu "f(print "a", print "b")" to bude nějak automaticky vyhodnocovat... a pak začneš řešit "ale co když to vyhodnotit nechci" a "v jakém pořadí se to má vyhodnotit".... není to trochu komplikace?
-
Ale opakuju otázku - co je na IO monádu složitého?
To se musíš ptát lidí, kteří mají problém to pochopit :)
kde se ukazuje, že to sice na začátku jako opičárna vypadala, ale vlastně to byl docela dobrý nápad.
Nemyslím si, že je opičárna ta volba pure-lazy. To je legitimní designové rozhodnutí, které ti lidi dělali určitě s dobrým vědomím a svědomím. Akorát to má prostě svoje důsledky, no. A jedním z těch důsledků je, že je ten jazyk pak těžko srozumitelný/použitelný pro BFP (běžný Franta programátor ;) ) To je celý, nic víc v těch mých příspěvcích nehledej :)
- podle tebe je celé IO opičárna, ale mě nějak vůbec není jasné, co by mělo být lepší.
Nic. Pokud máš pure jazyk, tak asi nic lepšího než IO monádu nevymyslíš. Ještě by možná stálo za řeč FRP - jakože by program jenom transformoval vstupy na výstupy. Ale to se hodí jenom na něco.
-
Tobě "do" syntaxe připadá složitá? Jediný, co potřebuješ pochopit, je rozdíl mezi pure a non-pure funkcí.
Žádné non-pure fce ale neexistují :)
No jasně, tak rozdíl mezi pure funkcí a IO akcí. Ale opakuju otázku - co je na IO monádu složitého? Ten monád je doslova (jak jsi psal) to, že pouští jeden "příkaz" za druhým. Žádné výhybky tam nejsou, žádné nedeterministické větvení, nic. Co na tom je složitého? Je to středník, který je naprogramovaný jako středník, takže "do" se chová tak, jak by člověk ve většině programovacích jazyků čekal ("return" na první pohled nevypadá jako šťastná volba, ale po několika kapitolách pure programování mě teda ani nenapadlo, že by to snad tu funkci mělo ukončit - to jsem fakt spíš bojoval s tím, kdy použít "let" a kdy "<-").
Taky se v tom začínám ztrácet, ale tak nějak ty tady v zásadě pořád říkáš, že laziness v zásadě způsobuje složitost haskellu a následně se takové věci jako řazení IO musí řešit přes monády. Já bych asi i souhlasil s tím, že to byl historický vývoj, ale dostali jsme se někam, kde se ukazuje, že to sice na začátku jako opičárna vypadala, ale vlastně to byl docela dobrý nápad.
- v pure výpočtech je laziness celkem putna, mnohdy je spíš lepší
- haskell je v zásadě celý pure včetně IO
- podle tebe je celé IO opičárna, ale mě nějak vůbec není jasné, co by mělo být lepší. Přestat si v IO hrát na pure programování, a pak když se rozhodneš, že místo IO použiješ nějaký Free, nebo to obalíš RWS, tak ten refaktoring bude znamenat přepisování do nějaké jiné syntaxe? Nebo že ti překladač bude automaticky detekovat, jestli je funkce v IO nebo ne a v nějaké syntaxi typu "f(print "a", print "b")" to bude nějak automaticky vyhodnocovat... a pak začneš řešit "ale co když to vyhodnotit nechci" a "v jakém pořadí se to má vyhodnotit".... není to trochu komplikace?
Co takhle psát česky? Je to TA monáda, ne ten monád...
-
strict: před vyhodnocením funkce jsou vyhodnoceny její argumenty
V tom případě by IO monádu nepotřeboval, protože řazení IO by pak šlo triviálně udělat takhle:
f(print "b",g(print "a"))
- dle té definice by pořadí výpisu nutně bylo a,b. Funkce f a g můžou dělat cokoli, může to být klidně identita.
Jak už zmínili jiní, tohle správné (pevné) pořadí nezajistí, nicméně funkcionální by bylo něco jako
print("a")("b")("c")
což ovšem nebude fungovat s každým typovým systémem.
-
print("a")("b")("c")
Jasně, napsal jsem to blbě, měl jsem namysli tohle:
print(c,print(b,print(a)))
což je skoro stejnej princip jako to, co píšeš.
Tak jako tak to ale byla zcestná argumentace, protože když se teda bavíme o plně čistém jazyce, tak vynucení pořadí vyhodnocování stejně k ničemu nevede (co se týče IO).
-
Je to akademický jazyk bez podpory imperativního programování, je to dobré na hraní si, ale v praxi je k ničemu.
Ty si taky v praxi k ničemu ... je to "pure" FP jazyk, co by tam mělo co dělat imperativní paradigma trotle? BTW ono tam skutečně je díky IO monádě, proto jsem to pure dal do uvozovek ... A tebe by taky chtělo dát do uvozovek
Naopak, ja jsem v praxi uzitecny, treba prave proto, ze nepouzivam haskell :-).
Napsal jsem, ze haskell je bez podpory imperativniho programovani, chces to snad rozporovat, trotle? Nebo s cim mas problem? Tenhle jazyk je akademicky a proto neprakticky, to je cele. Prakticke jazyky se snazi byt uzitecne, jsou pragmaticke a to jim brani byt akademicky ciste.
Na polozeny dotaz ohledne budoucnosti haskellu je tak jednoducha odpoved.
-
Napsal jsem, ze haskell je bez podpory imperativniho programovani, chces to snad rozporovat, trotle? Nebo s cim mas problem? Tenhle jazyk je akademicky a proto neprakticky, to je cele. Prakticke jazyky se snazi byt uzitecne, jsou pragmaticke a to jim brani byt akademicky ciste.
Co to znamená "bez podpory imperativního programování"? Fakt nevím, co je na "do" bloku neimperativního - tak "teoreticky" to je celé pure záležitost - ale v praxi to je normální imperativní procedura. Nikdo ti nebrání zapnout si Strict (měl jsem dojem, že to je už v 7.10.3, ale asi je to až ve verzi 8) a všechno dělat v IO přes "return". A jsi přesně tam, kde jsi u ostatních jazyků.
Ale ohledně praktičnosti - s výjimkou sem tam nějakého algoritmu, který závisí na mutability, bych řekl, že v haskellu v porovnání s "běžnými imperativními jazyky" - C, C++, C#, Java, Python - dokážu napsat totéž výrazně čistěji, se srovnatelnou výkonností a s méně chybami (there are obviously no errors vs. there are no obvious errors), a v případě refaktoringu to bude u haskellového kódu výrazně "neprůstřelnější".
-
Ale ohledně praktičnosti - s výjimkou sem tam nějakého algoritmu, který závisí na mutability, bych řekl, že v haskellu v porovnání s "běžnými imperativními jazyky" - C, C++, C#, Java, Python - dokážu napsat totéž výrazně čistěji, se srovnatelnou výkonností a s méně chybami (there are obviously no errors vs. there are no obvious errors), a v případě refaktoringu to bude u haskellového kódu výrazně "neprůstřelnější".
Tak tohle je výzva :). Já si kdysi zkoušel v Haskellu napsat https://en.wikipedia.org/wiki/Collatz_conjecture (https://en.wikipedia.org/wiki/Collatz_conjecture), to je taková hezká matematická hříčka, která by se měla pro Haskell hodit, ale Haskell to oproti C(++) prohrál na celé čáře. Buď to bylo v Haskellu čitelné, ale oproti C(++) řádově pomalejší, nebo to bylo pomalejší jen asi 2x, ale naprosto nečitelné. Dokážeš to v Haskellu napsat srovnatelně rychlé a čitelné?
-
Buď to bylo v Haskellu čitelné, ale oproti C(++) řádově pomalejší, nebo to bylo pomalejší jen asi 2x, ale naprosto nečitelné.
To není nic divnýho, u algoritmicky jednoduchých problémů, kde máš vytuněné řešení v C, bude každý vysokoúrovňový jazyk vždycky pomalejší. Protože prostě C má blíž k hardware, takže na těhle problémech moc není jak ho porazit.
Problémy ze skutečného světa ale nebývají algoritmicky jednoduché a na reálný výkon mají vliv jiné věci než hrubá rychlost výpočtu.
I v těchto umělých příkladech má ale Haskell výkonově blízko k Javě, což mi přijde dost dobrej výsledek na to, jak daleko je ten jazyk od hardware (sémanticky) - viz http://benchmarksgame.alioth.debian.org/u64q/haskell.html
-
Ale ohledně praktičnosti - s výjimkou sem tam nějakého algoritmu, který závisí na mutability, bych řekl, že v haskellu v porovnání s "běžnými imperativními jazyky" - C, C++, C#, Java, Python - dokážu napsat totéž výrazně čistěji, se srovnatelnou výkonností a s méně chybami (there are obviously no errors vs. there are no obvious errors), a v případě refaktoringu to bude u haskellového kódu výrazně "neprůstřelnější".
Tak tohle je výzva :). Já si kdysi zkoušel v Haskellu napsat https://en.wikipedia.org/wiki/Collatz_conjecture (https://en.wikipedia.org/wiki/Collatz_conjecture), to je taková hezká matematická hříčka, která by se měla pro Haskell hodit, ale Haskell to oproti C(++) prohrál na celé čáře. Buď to bylo v Haskellu čitelné, ale oproti C(++) řádově pomalejší, nebo to bylo pomalejší jen asi 2x, ale naprosto nečitelné. Dokážeš to v Haskellu napsat srovnatelně rychlé a čitelné?
Ach jo. Bavím se tady o PRAKTIČNOSTI haskellu, explicitně řeknu, že to opravdu na některé algoritmy, které vyžadují mutabilitu nefunguje a ty přijdeš s nějakým výpočtem, který se ti v C++ patrně vejde do cache a v haskellu třeba ne. Ano, jednoduché výpočetní algoritmy v těchhle jazycích fakt budou vypadat líp, počínaje quicksortem. Haskell má obrovské možnosti abstrakce, problém na 2 řádky opravdu není něco, na čem bys ten aparát haskellu mohl jakkoliv využít. Tohle je problém, který by byl přehledný i v assembleru.
Ale jen tak ze srandy bych si to zkusil, je zadání v long long nebo v arbitrary integer?
-
To není nic divnýho, u algoritmicky jednoduchých problémů, kde máš vytuněné řešení v C, bude každý vysokoúrovňový jazyk vždycky pomalejší. Protože prostě C má blíž k hardware, takže na těhle problémech moc není jak ho porazit.
Na tom není celkem co tunit, ten algoritmus je asi na 3 řádky. Ano je to algoritmicky jednoduché, ale když Haskell nedrží krok ani v jednoduchých čistě matematických nemutable algoritmech, proč bych měl věřit tomu, že to bude v něčem vysokoúrovňovém lepší? Jednoduché algoritmy jsou pomalé, ale složité budou rychlé? Nemyslím si to.
-
Na tom není celkem co tunit, ten algoritmus je asi na 3 řádky.
No tím "vytuněný" jsem myslel, že ten C kód se přeloží do nějakého optimálního strojáku. Tak je asi zřejmé, že nějaký "optimálnější než optimální" stroják asi těžko můžeš z jakéhokoliv jazyka dostat.
Ano je to algoritmicky jednoduché, ale když Haskell nedrží krok ani v jednoduchých čistě matematických nemutable algoritmech, proč bych měl věřit tomu, že to bude v něčem vysokoúrovňovém lepší? Jednoduché algoritmy jsou pomalé, ale složité budou rychlé? Nemyslím si to.
Nikdo tě nenutí si to myslet. Když tě Haskell neoslovuje a nevěříš mu, tak ho nepoužívej, co na tom chceš řešit? :)
-
Ach jo. Bavím se tady o PRAKTIČNOSTI haskellu, explicitně řeknu, že to opravdu na některé algoritmy, které vyžadují mutabilitu nefunguje a ty přijdeš s nějakým výpočtem, který se ti v C++ patrně vejde do cache a v haskellu třeba ne.
Collatz conjecture mutabilitu nepotřebuje. Do cache se to vejde v pohodě, pokud v Haskellu ne, je to problém Haskellu.
Ano, jednoduché výpočetní algoritmy v těchhle jazycích fakt budou vypadat líp, počínaje quicksortem.
V Haskellu se to taky dá napsat hezky a přehledně, jenom je to potom hrozně pomalé...
Haskell má obrovské možnosti abstrakce, problém na 2 řádky opravdu není něco, na čem bys ten aparát haskellu mohl jakkoliv využít. Tohle je problém, který by byl přehledný i v assembleru.
To je divný argument, Haskell má obrovské možnosti, a proto se na jednoduché věci nehodí? C++ má taky obrovské možnosti a hodí se i na jednoduché věci.
Ale jen tak ze srandy bych si to zkusil, je zadání v long long nebo v arbitrary integer?
To je celkem jedno, ale Int64 bude lepší.
-
To není nic divnýho, u algoritmicky jednoduchých problémů, kde máš vytuněné řešení v C, bude každý vysokoúrovňový jazyk vždycky pomalejší. Protože prostě C má blíž k hardware, takže na těhle problémech moc není jak ho porazit.
Na tom není celkem co tunit, ten algoritmus je asi na 3 řádky. Ano je to algoritmicky jednoduché, ale když Haskell nedrží krok ani v jednoduchých čistě matematických nemutable algoritmech, proč bych měl věřit tomu, že to bude v něčem vysokoúrovňovém lepší? Jednoduché algoritmy jsou pomalé, ale složité budou rychlé? Nemyslím si to.
S vyšší abstrakcí jde ruku v ruce nižší rychlost. FP je v některých případech ultimate win (parser combinator atp.) a někdy zas ultimate lose. BTW FP je schopno implicitního paralelismu programů, takže hej, se správným runtime na stroji s hodně jadry bude FP jazyk rychlej jak fretka. V jiném jazyku by ses pekloval s thready zatimco FP program by už běžel.
-
No tím "vytuněný" jsem myslel, že ten C kód se přeloží do nějakého optimálního strojáku. Tak je asi zřejmé, že nějaký "optimálnější než optimální" stroják asi těžko můžeš z jakéhokoliv jazyka dostat.
Však Haskell se taky překládá do nativního strojáku, kde je rozdíl?
Nikdo tě nenutí si to myslet. Když tě Haskell neoslovuje a nevěříš mu, tak ho nepoužívej, co na tom chceš řešit? :)
Nechci řešit nic, mně se Haskell celkem zalíbil, trochu jsem si s ním hrál, ale přitom přišlo celkem rozčarování. Výkon nic moc, občas mi to spadlo na velkých datech na stack overflow (když se Haskellu nepovedla nějaká tail recursion optimization). Jasně, je to moje chyba, měl jsem kód napsat líp, aby to Haskell správně pochopil a nespadl na stak overflow, ale takové chyby mě po pravdě děsí, v testu se to nemusí projevit a v produkci na velkých datech začne padat. Neviním z toho Haskell, aby bylo jasno, jednom ho prostě neumím dost dobře používat :).
-
S vyšší abstrakcí jde ruku v ruce nižší rychlost. FP je v některých případech ultimate win (parser combinator atp.) a někdy zas ultimate lose. BTW FP je schopno implicitního paralelismu programů, takže hej, se správným runtime na stroji s hodně jadry bude FP jazyk rychlej jak fretka. V jiném jazyku by ses pekloval s thready zatimco FP program by už běžel.
Nějaký praktický příklad, kdy bude FP jazyk rychlý jako fretka? Teorie je to hezká, ale nějaké praktické srovnání? Tyhle abstraktní výroky nemám rád. Jo až se nějaký hypotetický algoritmus pustí v FP na stoji v více jádry, to bude hukot... No asi bude... Co se na to dá říct?
-
Však Haskell se taky překládá do nativního strojáku, kde je rozdíl?
Rozdíl je v tom, že u Céčka je skoro přímá korespondence mezi zdrojákem a strojákem.
Výkon nic moc
Hrubý výkon sám o sobě je v dnešní době celkem podružná věc. Vždyť stejně dneska všechno jede na virtualizaci, ve které je ještě docker a komponenty spolu komunikují po TCP z jednoho kontejneru do druhého ;)
Pokud někde nutně potřebuješ výkon i za cenu dražšího vývoje a horší udržovatelnosti, tak prostě použiješ céčko.
občas mi to spadlo na velkých datech na stack overflow (když se Haskellu nepovedla nějaká tail recursion optimization).
Haskell v praxi nepoužívám, takže si nejsem jistý, ale tohle mi přijde hodně divný. Tail rekurze by se imho měla poznat vždycky. Problém bývá spíš v tom, že ten program je špatně napsaný, takže tam ta tail rekurze prostě není (je tam rekurze jinde než na konci).
Jasně, je to moje chyba, měl jsem kód napsat líp, aby to Haskell správně pochopil
Nejde o to, že by to nepochopil, ale o to, že ne-koncová rekurze se prostě optimalizovat nedá a na zásobník se ukládat musí.
-
Nějaký praktický příklad, kdy bude FP jazyk rychlý jako fretka? Teorie je to hezká, ale nějaké praktické srovnání? Tyhle abstraktní výroky nemám rád. Jo až se nějaký hypotetický algoritmus pustí v FP na stoji v více jádry, to bude hukot... No asi bude... Co se na to dá říct?
Co se na to dá říct? Že to je stejně obecné tvrzení jako "zkusil jsem to a bylo to pomalý". Pokud tě to opravdu zajímá, tak si prostě najdi nějaké testy. Nevím jak pro Haskell, ale pro Erlang by ti mělo stačit napsat do Googlu "erlang linear scalability test" a můžeš číst až do smrti :)
-
To není nic divnýho, u algoritmicky jednoduchých problémů, kde máš vytuněné řešení v C, bude každý vysokoúrovňový jazyk vždycky pomalejší. Protože prostě C má blíž k hardware, takže na těhle problémech moc není jak ho porazit.
Na tom není celkem co tunit, ten algoritmus je asi na 3 řádky. Ano je to algoritmicky jednoduché, ale když Haskell nedrží krok ani v jednoduchých čistě matematických nemutable algoritmech, proč bych měl věřit tomu, že to bude v něčem vysokoúrovňovém lepší? Jednoduché algoritmy jsou pomalé, ale složité budou rychlé? Nemyslím si to.
Jasně - chápu, že si to jedno kolečko v převodovce doma v dílně uděláš líp, než ti to vyplivne továrna. A teď se zkus zamyslet nad tím, kdo dokáže postavit lepší auto.
-
pro Erlang by ti mělo stačit napsat do Googlu "erlang linear scalability test" a můžeš číst až do smrti :)
... např.
https://www.youtube.com/watch?v=_jsMpmWaq7I
http://highscalability.com/blog/2014/2/26/the-whatsapp-architecture-facebook-bought-for-19-billion.html
https://people.cs.vt.edu/butta/cs4284/spring2011/butta/RabbitMQPaper.pdf
-
Hrubý výkon sám o sobě je v dnešní době celkem podružná věc. Vždyť stejně dneska všechno jede na virtualizaci, ve které je ještě docker a komponenty spolu komunikují po TCP z jednoho kontejneru do druhého ;)
Pokud někde nutně potřebuješ výkon i za cenu dražšího vývoje a horší udržovatelnosti, tak prostě použiješ céčko.
To není o C(++) versus Haskell, ale spíš cokoliv mainstreamového imperativního versus něco funkcionálního. Mně osobně to z hlediska nákladů na vývoj a udržovatelnost zatím vychází lépe pro imperativní jazyk. Na funkcionální jazyk neseženu lidi a když je seženu, tak budou dražší. Se zaučením bude taky problém, přijde mi, že funkcionální jazyky mají hodně plochou křivku učení.
-
Jasně - chápu, že si to jedno kolečko v převodovce doma v dílně uděláš líp, než ti to vyplivne továrna. A teď se zkus zamyslet nad tím, kdo dokáže postavit lepší auto.
Troufám si tvrdit, že C++ nebo Java nabízí minimálně stejně dobrou míru abstrakce jako Haskell a fungují jak pro jedno kolečko v převodovce, tak pro celé auto.
-
To není o C(++) versus Haskell, ale spíš cokoliv mainstreamového imperativního versus něco funkcionálního. Mně osobně to z hlediska nákladů na vývoj a udržovatelnost zatím vychází lépe pro imperativní jazyk. Na funkcionální jazyk neseženu lidi a když je seženu, tak budou dražší. Se zaučením bude taky problém, přijde mi, že funkcionální jazyky mají hodně plochou křivku učení.
To se spíš mýlíš. Nebo respektive jde o to, co chceš dělat. Opět: najdeš na webu různé success stories, kde CTO malé firmy práskl do stolu, zavedl FP a pochvaluje si to. Samozřejmě pokud seš mamutí firma s tisíci programátorů, kteří pro tebe nejsou nic než spotřební zboží, tak tam by to asi těžko fungovalo.
Zajímavá oblast, na které to je vidět, jsou třeba distribuované databáze. Každý rok se vyrojí minimálně jeden startup, který má relativně během chvilky napsaný v Erlangu nějaký DB engine s hodně zajímavými vlastnostmi. Oproti tomu rychlost vývoje klasických RDBMS se počítá spíš na eony ;)
-
Troufám si tvrdit, že C++ nebo Java nabízí minimálně stejně dobrou míru abstrakce jako Haskell
Tak urcite.
-
Jasně - chápu, že si to jedno kolečko v převodovce doma v dílně uděláš líp, než ti to vyplivne továrna. A teď se zkus zamyslet nad tím, kdo dokáže postavit lepší auto.
Troufám si tvrdit, že C++ nebo Java nabízí minimálně stejně dobrou míru abstrakce jako Haskell a fungují jak pro jedno kolečko v převodovce, tak pro celé auto.
Hmm... dobře. Tak teď já. Máš obrovský JSON soubor. Je to pole, vyskytují se v něm 2 typy záznamů. Třeba něco typu
{ "name": ..., "score":...} a { "organization": .., "score" .. }.
Chceš vypsat součet score, organizační skóre chceš násobit deseti. Tak v haskellu by to vypadalo cca takhle (z hlavy):
let parser = "name" .: string *> score .: integer
<|> (*10) <$> "organization" .: "string" *> "score" .: integer)
res <- readFile =$= parseIncremental parser =$= CL.fold (+) 0
Jak to zvládneš v C++?
-
Hmm... dobře. Tak teď já. Máš obrovský JSON soubor. Je to pole, vyskytují se v něm 2 typy záznamů. Třeba něco typu
{ "name": ..., "score":...} a { "organization": .., "score" .. }.
Chceš vypsat součet score, organizační skóre chceš násobit deseti. Tak v haskellu by to vypadalo cca takhle (z hlavy):
let parser = "name" .: string *> score .: integer
<|> (*10) <$> "organization" .: "string" *> "score" .: integer)
res <- readFile =$= parseIncremental parser =$= CL.fold (+) 0
Jak to zvládneš v C++?
Tohle je elegantní, uznávám, v C++ by to tak hezký nebylo. Ale je to taky dost speciální případ, funguje to jen díky tomu, že JSON je context-free jazyk. Pokud bys chtěl parsovat něco složitějšího, nebo kontrolovat validitu toho JSON souboru, tak stejně skončíš u nějakého obecnějšího parseru pro konkrétní formát dat.
-
¨Tohle je elegantní, uznávám, v C++ by to tak hezký nebylo. Ale je to taky dost speciální případ, funguje to jen díky tomu, že JSON je context-free jazyk. Pokud bys chtěl parsovat něco složitějšího, nebo kontrolovat validitu toho JSON souboru, tak stejně skončíš u nějakého obecnějšího parseru pro konkrétní formát dat.
Normální parser by vypadal třeba takhle:
data Item = Person { name :: Text, score :: Int} | Organization { name :: Text, score :: Int }
instance FromJSON Item where
parseJSON = withObject "Item" $ \obj -> parsePerson obj <|> parseOrg obj
where
parsePerson o = Person <$> o .: "name" <*> o .: "score"
parseOrg o = Organization <$> o .: "organization" <*> o .: "score"
No a tohle klidně můžeme vložit i do toho streaming parseru, takže máme třeba:
getScore User{score} = score
getScore Organization{score} = 10 * score
res <- runConduit $ readFile ... =$= parseIncremental (arrayOf value) =$= CL.map getScore =$= CL.fold (+) 0
A tohle je parser pro konkrétní formát dat. Ta FromJSON část je "monadická", tzn. je možné tam napsat jakoukoliv logiku. A tohle není speciální případ. Takhle to funguje ve většině haskellu. Dva často uváděné příklady - quickcheck a stm. Představ si, že chceš např. otestovat, jestli ti decode (encode x) je totéž. Např. pro výše uvedenou strukturu Item. To se hodí v momentě, kdy to dekódování nemáš generované automaticky. Tak takhle se to dělá v haskellu:
instance Arbitrary Item where
arbitrary = oneOf [ Person <$> arbitrary <*> arbitrary, Organization <$> arbitrary <*> arbitrary ]
enc_check :: (FromJSON a, ToJSON a, Eq a) => a -> Bool
enc_check = decode (encode a) == Just a
spec = do
describe "JSON structures" $ do
prop "Item" (jsonCheck :: enc_check -> Bool)
A test je hotový.
No a STM - software transactional memory - psal jsi někdy multithreadované aplikace? Tak třeba následující kód je úplně OK v multithreadovaném programu:
prevedPenize zdroj cil obnos =
atomically $ do
zustatek <- readTVar zdroj
if | zustatek >= obnos -> return False
| otherwise -> do
modifyTVar zdroj (subtract obnos)
modifyTVar cil (+ obnos)
return True
Tenhle kód je perfektně thread-safe. Žádné zámky, nic. Výkonnost? Ano, není to tak super-rychlé, jako kdybys to řešil v C++ se zámkama, ale pořád je to velmi slušně rychlé.
A tohle je jen nástin věcí, které lze s abstrakcema v haskellu udělat. To není žádný "speciální příklad", takhle vypadá normální kód a ty abstrakce se tam využívají. Hodně.
-
Buď to bylo v Haskellu čitelné, ale oproti C(++) řádově pomalejší, nebo to bylo pomalejší jen asi 2x, ale naprosto nečitelné.
To není nic divnýho, u algoritmicky jednoduchých problémů, kde máš vytuněné řešení v C, bude každý vysokoúrovňový jazyk vždycky pomalejší. Protože prostě C má blíž k hardware, takže na těhle problémech moc není jak ho porazit.
Problémy ze skutečného světa ale nebývají algoritmicky jednoduché a na reálný výkon mají vliv jiné věci než hrubá rychlost výpočtu.
I v těchto umělých příkladech má ale Haskell výkonově blízko k Javě, což mi přijde dost dobrej výsledek na to, jak daleko je ten jazyk od hardware (sémanticky) - viz http://benchmarksgame.alioth.debian.org/u64q/haskell.html
Srovnavat takto vysokourovnovy jazyk s C(++) neni opravdu fer. S Javou/C#/PHP/Pythonem nebo Ruby by to bylo ferovejsi. Fakt jsem netusil, ze Haskell je na tom tak dobre s rychlosti - neminim to ironicky, navic kdyz si vezmem, ze JVM ma za sebou urcite ladeni vykonu zaplacene obrovskymi korporacemi.
...C++ nebo Java nabízí minimálně stejně dobrou míru abstrakce jako Haskell...
o_O
-
Normální parser by vypadal třeba takhle:
Parsery vypadají v Haskellu hezky, uznávám.
No a STM - software transactional memory - psal jsi někdy multithreadované aplikace? Tak třeba následující kód je úplně OK v multithreadovaném programu:
prevedPenize zdroj cil obnos =
atomically $ do
zustatek <- readTVar zdroj
if | zustatek >= obnos -> return False
| otherwise -> do
modifyTVar zdroj (subtract obnos)
modifyTVar cil (+ obnos)
return True
Tenhle kód je perfektně thread-safe. Žádné zámky, nic. Výkonnost? Ano, není to tak super-rychlé, jako kdybys to řešil v C++ se zámkama, ale pořád je to velmi slušně rychlé.
V tomhle jednoduchém případě nevidím žádný benefit oproti:
bool prevedPenize(int& zdroj, int& cil, const int obnos)
{
std::lock_guard<std::mutex> lock(mutex);
if (zustatek >= obnos) return false;
zdroj -= obnos;
cil += obnos;
return true;
}
Na něco složitějšího se může STM hodit líp než zámky, ale nevidím rozdíl mezi C++ a Haskellem (ktomě rychlosti). V C++ si pro STM můžeš vybrat z asi 20 různých existujících libek, nebo použít built-in implemetaci v GCC (zatím experimentální): https://gcc.gnu.org/wiki/TransactionalMemory
-
Normální parser by vypadal třeba takhle:
Parsery vypadají v Haskellu hezky, uznávám.
No a STM - software transactional memory - psal jsi někdy multithreadované aplikace? Tak třeba následující kód je úplně OK v multithreadovaném programu:
prevedPenize zdroj cil obnos =
atomically $ do
zustatek <- readTVar zdroj
if | zustatek >= obnos -> return False
| otherwise -> do
modifyTVar zdroj (subtract obnos)
modifyTVar cil (+ obnos)
return True
Tenhle kód je perfektně thread-safe. Žádné zámky, nic. Výkonnost? Ano, není to tak super-rychlé, jako kdybys to řešil v C++ se zámkama, ale pořád je to velmi slušně rychlé.
V tomhle jednoduchém případě nevidím žádný benefit oproti:
bool prevedPenize(int& zdroj, int& cil, const int obnos)
{
std::lock_guard<std::mutex> lock(mutex);
if (zustatek >= obnos) return false;
zdroj -= obnos;
cil += obnos;
return true;
}
Na něco složitějšího se může STM hodit líp než zámky, ale nevidím rozdíl mezi C++ a Haskellem (ktomě rychlosti). V C++ si pro STM můžeš vybrat z asi 20 různých existujících libek, nebo použít built-in implemetaci v GCC (zatím experimentální): https://gcc.gnu.org/wiki/TransactionalMemory
int& vs. TVar Int
-
ale nevidím rozdíl mezi C++ a Haskellem (ktomě rychlosti). V C++ si pro STM můžeš vybrat z asi 20 různých existujících libek
No dost rozdíl je v tom, že v Haskellu je STM monáda jako každá jiná. Pokud použiješ některou z těch dvaceti libek, tak nejspíš dostaneš úplně nový typ, se kterým ti nebude fungovat nic (?) a pokud použiješ rozšíření jazyka, tak to už je úplně jiný příběh.
...tohle je právě o těch abstrakcích, které ti jazyk poskytuje nebo neposkytuje.
-
V tomhle jednoduchém případě nevidím žádný benefit oproti:
bool prevedPenize(int& zdroj, int& cil, const int obnos)
{
std::lock_guard<std::mutex> lock(mutex);
if (zustatek >= obnos) return false;
zdroj -= obnos;
cil += obnos;
return true;
}
Na něco složitějšího se může STM hodit líp než zámky, ale nevidím rozdíl mezi C++ a Haskellem (ktomě rychlosti).
Tak třeba rozdíl je, že ty používáš global lock a já v haskellu ne. Další rozdíl je v tom, že mě do té proměnné nikdo mimo "atomically" block nešáhne. Tobě to klidně někdo změní někde jinde v kódu - a hledej. Co když potřebuješ zamknout zámky 2? Ještě pořád triviální? A teď úplně jednoduchá modifikace - místo vrácení "nedostatek na účtu" čekej. A třeba čekej nějakou maximální dobu....
maybe (return True) (const False) $ timeout (jak_dlouho) $ atomically $ do
zustatek <- readTVar zdroj
guard $ zustatek >= obnos
modifyTVar zdroj (subtract obnos)
modifyTVar cil (+ obnos)
Bezpečné proti výjimkám, překladač kontroluje, že z toho atomically blocku nevoláš něco mimo STM... Fakt ti to připadá jako totéž?
-
int& vs. TVar Int
A? V C++ se zámkem nemusím řešit, kde ta proměnná fyzicky leží v paměti. Může být v úplně normální paměti, nemusí být v žádné speciální transakční. Místo int& tam můžu klidně strčit class Ucet& a taky to bude fungovat, stačí když pro Ucet přetížím operátor + a -.
Ale tohle se stejně akademický případ, reálně ty data budou v SQL databázi a bude se to řešit transakcemi na úrovni databáze.
-
int& vs. TVar Int
A? V C++ se zámkem nemusím řešit, kde ta proměnná fyzicky leží v paměti. Může být v úplně normální paměti, nemusí být v žádné speciální transakční. Místo int& tam můžu klidně strčit class Ucet& a taky to bude fungovat, stačí když pro Ucet přetížím operátor + a -.
Ale tohle se stejně akademický případ, reálně ty data budou v SQL databázi a bude se to řešit transakcemi na úrovni databáze.
:-)
-
Tak třeba rozdíl je, že ty používáš global lock a já v haskellu ne.
To není žádný global lock, je to jeden konkrétní mutex. I v Haskellu to bude interně implementováno nějakým zámkem (pokud na to nebudou stačit atomické operace), nebo nějakou post sychronizací více transakcí (to si moc nedokážu představit, ale asi by to taky šlo).
Další rozdíl je v tom, že mě do té proměnné nikdo mimo "atomically" block nešáhne.
V C++ to v reálné implementaci zapouzdříš do nějaké třídy, ve které budou ty proměnné i mutex. Taky na to zvenku nikdo nesáhne.
Tobě to klidně někdo změní někde jinde v kódu - a hledej.
Nezmění, když to máš správně navrženo.
A teď úplně jednoduchá modifikace - místo vrácení "nedostatek na účtu" čekej. A třeba čekej nějakou maximální dobu....
maybe (return True) (const False) $ timeout (jak_dlouho) $ atomically $ do
zustatek <- readTVar zdroj
guard $ zustatek >= obnos
modifyTVar zdroj (subtract obnos)
modifyTVar cil (+ obnos)
Bezpečné proti výjimkám, překladač kontroluje, že z toho atomically blocku nevoláš něco mimo STM... Fakt ti to připadá jako totéž?
Něco takového?
bool prevedPenize(int& zdroj, int& cil, const int obnos, const int timeout)
{
try {
std::lock_guard<std::mutex> lock(mutex);
if (zustatek >= obnos) throw std::runtime_error("cekej");
zdroj -= obnos;
cil += obnos;
return true;
}
catch (const std::exception& e) {
wait(timeout);
}
return false;
}
-
Něco takového?
bool prevedPenize(int& zdroj, int& cil, const int obnos, const int timeout)
{
try {
std::lock_guard<std::mutex> lock(mutex);
if (zustatek >= obnos) throw std::runtime_error("cekej");
zdroj -= obnos;
cil += obnos;
return true;
}
catch (const std::exception& e) {
wait(timeout);
}
return false;
}
ne, musel byste locknout, zkontrolovat podmínku, pokud neplatí tak unlocknout a tak dokola dokud nedojde čas nebo se podmínka nesplní
-
Mozna to chapu spatne (neznam moc ani C++ ani Haskell), ale dela ta C++ verze opravdu to stejne? Bych cekal, ze ta C++ verze na guardu spadne, pocka, ale znovu to nezkusi, zatimco ta Haskelli na guardu pocka a kdyz se cekani zadari, tak to provede.
EDIT: ah, v me predbeh :D
-
To není žádný global lock, je to jeden konkrétní mutex. I v Haskellu to bude interně implementováno nějakým zámkem (pokud na to nebudou stačit atomické operace), nebo nějakou post sychronizací více transakcí (to si moc nedokážu představit, ale asi by to taky šlo).
Je to implementováno tak, že se v atomic bloku při přístupu k TVarům zapisuje transakční log a na konci transakce se to zpracuje. Teď koukám do svého kódu - např. jsem potřeboval napsat, aby došlo k restartům některých threadů v případě změny konfigurace. Konfiguraci mám v TVaru. Kód:
tvarWatcher = do
config <- atomically $ readTVar confTvar
restartServices config
atomically $ do
newconf <- readTVar confTvar
unless (config /= newconf) retry
tvarWatcher
Žádná nutnost pro nějaké signály, broadcasty apod.
V C++ to v reálné implementaci zapouzdříš do nějaké třídy, ve které budou ty proměnné i mutex. Taky na to zvenku nikdo nesáhne.
Jasně, a přístup ke dvěma takovým proměnným ti rozhodně nikdy nezpůsobí deadlock...
Něco takového?
Ne, můj kód čeká požadovaný timeout, zda-li se v té době podmínka nesplní. Tvůj ne. Tohle přes zámky neuděláš, na to potřebuješ condition variable... se 2ma proměnnýma. Lahůdka.
-
Jasně, a přístup ke dvěma takovým proměnným ti rozhodně nikdy nezpůsobí deadlock...
Pokud tu třídu neuděláš blbě, tak ti to deadlock nezpůsobí. Když ji uděláš blbě, tak samozřejmě deadlock vyrobit můžeš. I když deadlock s jedním mutexem, to už musí být vyšší dívčí, aby se to povedlo.
Nicméně ty pořád operuješ deadlocky s mutexy, jo to se může stát, ale když nechci, nemusím mutexy v C++ vůbec používat. Mám k dispozici úplně stejná sychronizační primitiva jako v Haskellu (atomické proměnné, shared memory, mutexy, thread specific variables, STM...). Je jenom na mně, co použiju. A že si můžu nabít čumák, když něco použitju blbě? Ano můžu.
Ne, můj kód čeká požadovaný timeout, zda-li se v té době podmínka nesplní. Tvůj ne. Tohle přes zámky neuděláš, na to potřebuješ condition variable... se 2ma proměnnýma. Lahůdka.
Jo tohle, to je C++ taky triviální, normální mutex: http://en.cppreference.com/w/cpp/thread/timed_mutex
-
Jo tohle, to je C++ taky triviální, normální mutex: http://en.cppreference.com/w/cpp/thread/timed_mutex
ne, to není ono
-
Pokud tu třídu neuděláš blbě, tak ti to deadlock nezpůsobí. Když ji uděláš blbě, tak samozřejmě deadlock vyrobit můžeš. I když deadlock s jedním mutexem, to už musí být vyšší dívčí, aby se to povedlo.
...
Mám k dispozici úplně stejná sychronizační primitiva jako v Haskellu (atomické proměnné, shared memory, mutexy, thread specific variables, STM...)
...
Jo tohle, to je C++ taky triviální, normální mutex: http://en.cppreference.com/w/cpp/thread/timed_mutex
Koukám, že nemáš moc zkušeností s psaním paralelních programů. Jakmile máš více než 1 mutex, musíš si dávat majzla, v jakém pořadí je zamkneš. Takže buď skončíš na globálním mutexu, nebo ne, ale v tom případě hodně štěstí. Já to s STM navrhovat nemusím. Neřeším. Prostě se nestane - za cenu o něco menší výkonnosti.
Stejná synchronizační primitiva sice máš, ale zas je to srovnání, jako že ty v té dílně máš taky v podstatě stejné nástroje k dispozici, jako ten CAM stroj... zkoušel jsi s tém někdy pracovat?
A jak psal v - podívej se na condition variable, a pak si zkus napsat kód, který dělá to samé... a pak se zamysli, jestli je něco takového vůbec srovnatelné s tím, co bez problému napíšeš v haskellu....
-
Oh C'mon ... opravdu se tady někdo snaží dokazovat úžasnost haskellu na příkladu prográmku kterej používá mutable sračky a IO monádu? Jako vážně? Fakt sem nepište, uděláte dobře, svět bude míň blbej.
Jó to kdyby se tady někdo pokoušel ukázat sílu Haskellu na nějaké rozvětvené rekurzy využívající `par`, to by byla jiná.
Porovnávat C++ a Haskell je kompletně mimo. Implementují jiná paradigmata a slouží pro úplně jiné aplikace. Jestli se naseru, tak sem natáhnu nějakýho Javystu, ať je aspoň sranda.
-
Pokud tu třídu neuděláš blbě, tak ti to deadlock nezpůsobí. Když ji uděláš blbě, tak samozřejmě deadlock vyrobit můžeš. I když deadlock s jedním mutexem, to už musí být vyšší dívčí, aby se to povedlo.
...
Mám k dispozici úplně stejná sychronizační primitiva jako v Haskellu (atomické proměnné, shared memory, mutexy, thread specific variables, STM...)
...
Jo tohle, to je C++ taky triviální, normální mutex: http://en.cppreference.com/w/cpp/thread/timed_mutex
Koukám, že nemáš moc zkušeností s psaním paralelních programů. Jakmile máš více než 1 mutex, musíš si dávat majzla, v jakém pořadí je zamkneš. Takže buď skončíš na globálním mutexu, nebo ne, ale v tom případě hodně štěstí. Já to s STM navrhovat nemusím. Neřeším. Prostě se nestane - za cenu o něco menší výkonnosti.
Stejná synchronizační primitiva sice máš, ale zas je to srovnání, jako že ty v té dílně máš taky v podstatě stejné nástroje k dispozici, jako ten CAM stroj... zkoušel jsi s tém někdy pracovat?
A jak psal v - podívej se na condition variable, a pak si zkus napsat kód, který dělá to samé... a pak se zamysli, jestli je něco takového vůbec srovnatelné s tím, co bez problému napíšeš v haskellu....
OMG, tak si tu STM sračku udělá i v C++ ... STM nemaj nic do činění s FP ...
-
ne, to není ono
Dobře já to rozepíšu, jen nástřel:
std::timed_mutex mutex;
...
while (current_time < max_time) {
mutex.try_lock_for(std::chrono::milliseconds(100));
if (zustatek > obnos) {
zdroj -= obnos;
cil += obnos;
mutex.unlock();
return true;
}
}
return false;
stačí jeden mutex.
-
ne, to není ono
Dobře já to rozepíšu, jen nástřel:
std::timed_mutex mutex;
...
while (current_time < max_time) {
mutex.try_lock_for(std::chrono::milliseconds(100));
if (zustatek > obnos) {
zdroj -= obnos;
cil += obnos;
mutex.unlock();
return true;
}
}
return false;
stačí jeden mutex.
Fajn, a teď zruš to aktivní čekání a udělej pasivní.
-
ne, to není ono
Dobře já to rozepíšu, jen nástřel:
std::timed_mutex mutex;
...
while (current_time < max_time) {
mutex.try_lock_for(std::chrono::milliseconds(100));
if (zustatek > obnos) {
zdroj -= obnos;
cil += obnos;
mutex.unlock();
return true;
}
}
return false;
stačí jeden mutex.
neměl byste kontrolovat návratovou hodnotu try_lock_for?
-
Koukám, že nemáš moc zkušeností s psaním paralelních programů. Jakmile máš více než 1 mutex, musíš si dávat majzla, v jakém pořadí je zamkneš. Takže buď skončíš na globálním mutexu, nebo ne, ale v tom případě hodně štěstí. Já to s STM navrhovat nemusím. Neřeším. Prostě se nestane - za cenu o něco menší výkonnosti.
Multithread věcí jsem už C++ řešil dost, myslím že o tom něco málo vím. Ale nechci si tady poměřovat velikosti zdrojových kódů, myslím že je to zbytečné. Je to celé o tom, jak to navrhneš. Dva mutexy můžou být problém, musíš si dávat pozor. Ty se toho bojíš, takže mutexy radši vůbec nepoužíváš. Já se toho taky bojím a když už něco paralelního dělám, snažím se mít v rámci kontextu jen jeden mutex. Když si řeknu, že už to mentálně nedávám, použiju místo mutexů něco jiného, třeba STM.
A jak psal v - podívej se na condition variable, a pak si zkus napsat kód, který dělá to samé... a pak se zamysli, jestli je něco takového vůbec srovnatelné s tím, co bez problému napíšeš v haskellu....
Na tohle primitvní čekání, jestli je na účtu dost peněz, bych condition variable v C++ asi nepoužil, je to zbytečně složité.
-
Porovnávat C++ a Haskell je kompletně mimo. Implementují jiná paradigmata a slouží pro úplně jiné aplikace. Jestli se naseru, tak sem natáhnu nějakýho Javystu, ať je aspoň sranda.
Tak je fakt, že z C++ se stal takový lepší systémový jazyk... má to blízko k HW a je to lepší než C. Ale je to škoda - proč by nějaký microservice webová aplikace nemohla být napsaná v C++?
OMG, tak si tu STM sračku udělá i v C++ ... STM nemaj nic do činění s FP ...
STM tak, jak je implementované v Haskellu, je prakticky nemožné udělat v jazyku bez typů, které jsou v Haskellu. Rozhodně ne s ani vzdáleně srovnatelnou mírou bezpečnosti.
-
neměl byste kontrolovat návratovou hodnotu try_lock_for?
Jasně že měl, byl to jen nástřel, jak to udělat, bez nároku na funkčnost :).
-
Multithread věcí jsem už C++ řešil dost, myslím že o tom něco málo vím. Ale nechci si tady poměřovat velikosti zdrojových kódů, myslím že je to zbytečné. Je to celé o tom, jak to navrhneš. Dva mutexy můžou být problém, musíš si dávat pozor. Ty se toho bojíš, takže mutexy radši vůbec nepoužíváš. Já se toho taky bojím a když už něco paralelního dělám, snažím se mít v rámci kontextu jen jeden mutex. Když si řeknu, že už to mentálně nedávám, použiju místo mutexů něco jiného, třeba STM.
Já se toho nebojím. Já nechci ztrácet čas a nervy a energii s tím dělat něco složitě, když to jde jednoduše.
Na tohle primitvní čekání, jestli je na účtu dost peněz, bych condition variable v C++ asi nepoužil, je to zbytečně složité.
QED?
-
Porovnávat C++ a Haskell je kompletně mimo. Implementují jiná paradigmata a slouží pro úplně jiné aplikace. Jestli se naseru, tak sem natáhnu nějakýho Javystu, ať je aspoň sranda.
Tak je fakt, že z C++ se stal takový lepší systémový jazyk... má to blízko k HW a je to lepší než C. Ale je to škoda - proč by nějaký microservice webová aplikace nemohla být napsaná v C++?
OMG, tak si tu STM sračku udělá i v C++ ... STM nemaj nic do činění s FP ...
STM tak, jak je implementované v Haskellu, je prakticky nemožné udělat v jazyku bez typů, které jsou v Haskellu. Rozhodně ne s ani vzdáleně srovnatelnou mírou bezpečnosti.
STM je jen nějakej kus abstrakce nad atomickejma instrukcema, nic víc nic míň, to samí můžeš mít i v C++, Jave, Ruby a já nevím v čem jiným. Pokud v něčem není Haskell unikátní, tak v tomhle. Děláš Haskellu vcelku blbou službu.
-
Já se toho nebojím. Já nechci ztrácet čas a nervy a energii s tím dělat něco složitě, když to jde jednoduše.
V pohodě, je to tvoje volba, když chceš STM, používej STM, nic proti tomu nemám. Dělal jsem v C++ věci, u kterých byl kritický výkon a STM nebyla volba. I u mutexu se často řešila režije a hledala se nějaká lockless alternativa.
QED?
Matematický důkaz ti nedám, je to prostě můj subjektivní dojem. Než se drbat s condition variables mezi thready, tak na takové jednoduché čekání prostě použiju timed_mutex s nějakým rozumným sleepem. Není to úplně ideální řešení, protože když je tam ten sleep, jsou tam nějaké probuzení threadu navíc a o změně se dozvíš až se zpožděním sleepu, ale pokud to nevadí, je pro mě timed_mutex jasná volba.
-
STM tak, jak je implementované v Haskellu, je prakticky nemožné udělat v jazyku bez typů, které jsou v Haskellu. Rozhodně ne s ani vzdáleně srovnatelnou mírou bezpečnosti.
STM je jen nějakej kus abstrakce nad atomickejma instrukcema, nic víc nic míň, to samí můžeš mít i v C++, Jave, Ruby a já nevím v čem jiným. Pokud v něčem není Haskell unikátní, tak v tomhle. Děláš Haskellu vcelku blbou službu.
Mohl bys mi ukázat kus kódu STM v C++, Javě nebo Ruby, který je srovnatelný z hlediska type-checking, composability apod. s Haskellem? Já to neznám, rád se poučím.
-
Matematický důkaz ti nedám, je to prostě můj subjektivní dojem. Než se drbat s condition variables mezi thready, tak na takové jednoduché čekání prostě použiju timed_mutex s nějakým rozumným sleepem. Není to úplně ideální řešení, protože když je tam ten sleep, jsou tam nějaké probuzení threadu navíc a o změně se dozvíš až se zpožděním sleepu, ale pokud to nevadí, je pro mě timed_mutex jasná volba.
Já to myslel jinak:
Troufám si tvrdit, že C++ nebo Java nabízí minimálně stejně dobrou míru abstrakce jako Haskell a fungují jak pro jedno kolečko v převodovce, tak pro celé auto.
Tak jenom, že jsi právě prohlásil, že v C++ použiješ v té převodovce plastové kolečko, protože použít kovové je strašný opruz. Tak nějak se mi víc líbí to auto v haskellu...
-
Ohledně otázky, zdali je pro člověka přirozenější funkcionální nebo imperativní paradigma je myslím odpověď jasná. Stačí nebejt "provinční" a zvednout hlavu od monitoru.
V jaké formě je psaná každá kuchařka? Ve stylu:
= rozděl vejce na žloutek a bílek
= bílek utři na sníh
= přimíchej tam žloutek, cukr a mouku a olej
= nalejte do formy
= pečte hodinu při 200 stupních
Nebo stylem:
= bílek a žloutek získáte rozdělením vejce
= sníh získáte ušleháním bílku
= těsto na bábovku získáte smíšením sněhu, žloutku, cukru a mouky a oleje
= neupečenou bábovku získáte nalitím těsta do formy
= bábovku získáte hodinovým pečením neupečené bábovky při 200 stupních.
(ideálně ještě nikoliv tomto pořadí :-))
?
No, mne treba pripada to druhe lepsi... Podle me zalezi, jestli umis varit, nebo ne (i kdyz sam varit neumim). Zkuseny clovek vi jak ziskat zloutek a jak uvarit babovku, takze se proste soustredi na tu prostredni vec a nemusi cist cely recept. :-)
Podobny priklad dava Dave Thomas ve svem slavnem "Herding horses, racing sheep" - mluvi o tom, jak si lide s ruznou urovni zkusenosti zapisuji recepty. U zacatecnika je to jak pises. U experta by to bylo treba jenom - testo jako na babovku, akorat se tam jeste prida tohle.
-
Mohl bys mi ukázat kus kódu STM v C++, Javě nebo Ruby, který je srovnatelný z hlediska type-checking, composability apod. s Haskellem? Já to neznám, rád se poučím.
Tak třeba podpora v GCC: https://gcc.gnu.org/wiki/TransactionalMemory, je to implementováno podle toho Draft Specification of Transactional Language Constructs for C++ dole v odkazu.
Ale v C++ se STM zase tak moc nepoužívá, není moc velká poptávka. V C++ všichni řeší výkon a STM do toho úplně nepasuje.
-
Mohl bys mi ukázat kus kódu STM v C++, Javě nebo Ruby, který je srovnatelný z hlediska type-checking, composability apod. s Haskellem? Já to neznám, rád se poučím.
Tak třeba podpora v GCC: https://gcc.gnu.org/wiki/TransactionalMemory, je to implementováno podle toho Draft Specification of Transactional Language Constructs for C++ dole v odkazu.
Ale v C++ se STM zase tak moc nepoužívá, není moc velká poptávka. V C++ všichni řeší výkon a STM do toho úplně nepasuje.
Nepřipadá mi, že by to umělo retry, jak je použito v tom příkladu výše? Pletu se?
-
Nepřipadá mi, že by to umělo retry, jak je použito v tom příkladu výše? Pletu se?
Nepleteš se, retry to neumí, ale zkus přijmout, že retry i případný timeout je naprosto minoritní funkcionalita, kterou si snadno doděláš. Trochu jsem si s tím v C++ hrál a zajímalo by mě, jestli tohle dokážeš napsat stejně elegantně a abstraktně i v Haskellu? Uznávám, že je to trochu školometský příklad:
#include <string>
#include <thread>
#include <iostream>
template<typename T>
class ThreadSafeClass
{
public:
ThreadSafeClass() : m_value(T()) {}
T get() const {
return m_value;
}
ThreadSafeClass& operator += (const T value) {
__transaction_relaxed { m_value += value; }
return *this;
}
private:
T m_value;
};
template<typename T>
void updateThread(ThreadSafeClass<T>& tsc, const T value)
{
for (int i = 0; i < 100; ++i) {
tsc += value;
}
}
int main()
{
ThreadSafeClass<int> safeInt;
ThreadSafeClass<double> safeDouble;
ThreadSafeClass<std::string> safeString;
std::thread t1([&]{updateThread(safeInt, 1);});
std::thread t2([&]{updateThread(safeInt, 2);});
std::thread t3([&]{updateThread(safeDouble, 1.0);});
std::thread t4([&]{updateThread(safeDouble, 2.0);});
std::thread t5([&]{updateThread(safeString, std::string("A"));});
std::thread t6([&]{updateThread(safeString, std::string("B"));});
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
t6.join();
std::cout << safeInt.get() << std::endl;
std::cout << safeDouble.get() << std::endl;
std::cout << safeString.get() << std::endl;
return 0;
}
Obecná třída pro thread safe operaci + nad libovolným datovým typem (včetně stringu), máš to kompletní i s testem. Pokud vím, v Haskellu to musí být TVar? V C++ žádné takové omezení není, může to být libovolná proměnná.
-
Nepřipadá mi, že by to umělo retry, jak je použito v tom příkladu výše? Pletu se?
Nepleteš se, retry to neumí, ale zkus přijmout, že retry i případný timeout je naprosto minoritní funkcionalita, kterou si snadno doděláš. Trochu jsem si s tím v C++ hrál a zajímalo by mě, jestli tohle dokážeš napsat stejně elegantně a abstraktně i v Haskellu? Uznávám, že je to trochu školometský příklad:
#include <string>
#include <thread>
#include <iostream>
template<typename T>
class ThreadSafeClass
{
public:
ThreadSafeClass() : m_value(T()) {}
T get() const {
return m_value;
}
ThreadSafeClass& operator += (const T value) {
__transaction_relaxed { m_value += value; }
return *this;
}
private:
T m_value;
};
template<typename T>
void updateThread(ThreadSafeClass<T>& tsc, const T value)
{
for (int i = 0; i < 100; ++i) {
tsc += value;
}
}
int main()
{
ThreadSafeClass<int> safeInt;
ThreadSafeClass<double> safeDouble;
ThreadSafeClass<std::string> safeString;
std::thread t1([&]{updateThread(safeInt, 1);});
std::thread t2([&]{updateThread(safeInt, 2);});
std::thread t3([&]{updateThread(safeDouble, 1.0);});
std::thread t4([&]{updateThread(safeDouble, 2.0);});
std::thread t5([&]{updateThread(safeString, std::string("A"));});
std::thread t6([&]{updateThread(safeString, std::string("B"));});
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
t6.join();
std::cout << safeInt.get() << std::endl;
std::cout << safeDouble.get() << std::endl;
std::cout << safeString.get() << std::endl;
return 0;
}
Obecná třída pro thread safe operaci + nad libovolným datovým typem (včetně stringu), máš to kompletní i s testem. Pokud vím, v Haskellu to musí být TVar? V C++ žádné takové omezení není, může to být libovolná proměnná.
TVar je vlastně ekivalent toho vašeho ThreadSafeClass, s rozdílem, že TVar nemá chybu ve čtení ve čtení hodnoty a je možné na obsah použít libovolnou operaci, ne jen +
-
TVar je vlastně ekivalent toho vašeho ThreadSafeClass, s rozdílem, že TVar nemá chybu ve čtení ve čtení hodnoty a je možné na obsah použít libovolnou operaci, ne jen +
Můžeš sem dát kód v Haskellu, který pro libovolný datový typ podporující operaci + přičtě něco ve více threadech a bude používat STM?
-
TVar je vlastně ekivalent toho vašeho ThreadSafeClass, s rozdílem, že TVar nemá chybu ve čtení ve čtení hodnoty a je možné na obsah použít libovolnou operaci, ne jen +
Můžeš sem dát kód v Haskellu, který pro libovolný datový typ podporující operaci + přičtě něco ve více threadech a bude používat STM?
vyšlo mi to takhle:
import Control.Concurrent.Async
import Control.Concurrent.STM
incrementTVar v k = atomically (modifyTVar' v (+ k))
tVarIncrementingProcess v k = sequence (replicate 100 (incrementTVar v k))
main = do
v0 <- newTVarIO 0 :: IO (TVar Integer)
p0 <- async (tVarIncrementingProcess v0 10)
wait p0
-
v0 <- newTVarIO 0 :: IO (TVar Integer)
p0 <- async (tVarIncrementingProcess v0 10)
Tohle bude fungovat i pro string? Nebo nějaký jiný typ, pro který má smysl sčítat (vektor, matice...)? Co dělám blbě?
v0 <- newTVarIO "" :: IO (TVar String)
p0 <- async (tVarIncrementingProcess v0 "A")
No instance for (Num String)
arising from a use of `tVarIncrementingProcess'
Possible fix: add an instance declaration for (Num String)
In the first argument of `async', namely
`(tVarIncrementingProcess v0 "A")'
In a stmt of a 'do' block:
p0 <- async (tVarIncrementingProcess v0 "A")
In the expression:
do { v0 <- newTVarIO "" :: IO (TVar String);
p0 <- async (tVarIncrementingProcess v0 "A");
wait p0 }
-
v0 <- newTVarIO 0 :: IO (TVar Integer)
p0 <- async (tVarIncrementingProcess v0 10)
Tohle bude fungovat i pro string? Nebo nějaký jiný typ, pro který má smysl sčítat (vektor, matice...)? Co dělám blbě?
v0 <- newTVarIO "" :: IO (TVar String)
p0 <- async (tVarIncrementingProcess v0 "A")
No instance for (Num String)
arising from a use of `tVarIncrementingProcess'
Possible fix: add an instance declaration for (Num String)
In the first argument of `async', namely
`(tVarIncrementingProcess v0 "A")'
In a stmt of a 'do' block:
p0 <- async (tVarIncrementingProcess v0 "A")
In the expression:
do { v0 <- newTVarIO "" :: IO (TVar String);
p0 <- async (tVarIncrementingProcess v0 "A");
wait p0 }
Vzdyt ti to ta chybova hlaska rika - String neni instance Num. Retezeni stringu ostatne neni v Haskellu + ale ++
-
Vzdyt ti to ta chybova hlaska rika - String neni instance Num. Retezeni stringu ostatne neni v Haskellu + ale ++
OK, jak to teda mám v Haskellu napsat, aby to fungovalo s libovolným datovým typem? V C++ to jde.
-
Vzdyt ti to ta chybova hlaska rika - String neni instance Num. Retezeni stringu ostatne neni v Haskellu + ale ++
OK, jak to teda mám v Haskellu napsat, aby to fungovalo s libovolným datovým typem? V C++ to jde.
záleží co od toho vlastně chcete, jaký problém tím chcete vyřešit, řetězce se nesčítají, ale řetězí, taky můžete použít mappend, ale to je zase něco jiného, nebo těm vláknům rovnou předat funkci, kterou chcete aplikovat
-
záleží co od toho vlastně chcete, jaký problém tím chcete vyřešit, řetězce se nesčítají, ale řetězí, taky můžete použít mappend, ale to je zase něco jiného, nebo těm vláknům rovnou předat funkci, kterou chcete aplikovat
No já bych chtěl obecné řešení na sčítání datových typů, které se sčítat umí. Jsem ochoten smířit se s tím, že string se v Haskellu sčítat neumí. Co třeba vektor, šlo by to? Neznám tu správnou syntaxi v Haskellu, něco takového by fungovalo?
import Data.Vector
main = do
let v1 = fromList [0, 0]
let v2 = fromList [1, 1]
v0 <- newTVarIO v1 :: IO (TVar Data.Vector)
p0 <- async (tVarIncrementingProcess v0 v2)
wait p0
-
záleží co od toho vlastně chcete, jaký problém tím chcete vyřešit, řetězce se nesčítají, ale řetězí, taky můžete použít mappend, ale to je zase něco jiného, nebo těm vláknům rovnou předat funkci, kterou chcete aplikovat
No já bych chtěl obecné řešení na sčítání datových typů, které se sčítat umí. Jsem ochoten smířit se s tím, že string se v Haskellu sčítat neumí. Co třeba vektor, šlo by to? Neznám tu správnou syntaxi v Haskellu, něco takového by fungovalo?
import Data.Vector
main = do
let v1 = fromList [0, 0]
let v2 = fromList [1, 1]
v0 <- newTVarIO v1 :: IO (TVar Data.Vector)
p0 <- async (tVarIncrementingProcess v0 v2)
wait p0
pro použití funcke (+) musíte mít nadefinovanou pro daný type třídu Num (ve skutečnosti nemusíte, ale slušní lidi to tak dělají)
já jsem ji pro vás napsal pro String :D
{-# LANGUAGE FlexibleInstances #-}
instance Num [Char] where
(+) = (++)
(*) _ _ = error "na tohle je už dneska moc hodin"
abs = id
signum _ = ":-)"
fromInteger = show
negate = reverse
asi to není problém pro žádný typ, ale někdy to moc nedává smysl
-
pro použití funcke (+) musíte mít nadefinovanou pro daný type třídu Num (ve skutečnosti nemusíte, ale slušní lidi to tak dělají)
já jsem ji pro vás napsal pro String :D
{-# LANGUAGE FlexibleInstances #-}
instance Num [Char] where
(+) = (++)
(*) _ _ = error "na tohle je už dneska moc hodin"
abs = id
signum _ = ":-)"
fromInteger = show
negate = reverse
asi to není problém pro žádný typ, ale někdy to moc nedává smysl
To je to řešení, které jsem hledal, díky. Připadá mi to teda trochu jako hack, ale budiž, když to bude fungovat... C++ mi z toho vychází líp, nemusím takové opičárny dělat, stačí aby se ten objekt uměl sčítat. Třeba pro vektor bez obalovací classy, která se nelíbila:
#include <string>
#include <thread>
#include <iostream>
#include <boost/numeric/ublas/vector.hpp>
template<typename T>
void updateThread(T& base, const T& value)
{
for (int i = 0; i < 100; ++i) {
__transaction_relaxed { base += value; }
}
}
int main()
{
boost::numeric::ublas::vector<double> v1(2);
boost::numeric::ublas::vector<double> v2(2);
std::thread t1([&]{updateThread(v1, v2);});
std::thread t2([&]{updateThread(v1, v2);});
t1.join();
t2.join();
return 0;
}
Líbí se mi to víc, než definovat Num v Haskellu.
-
No já bych chtěl obecné řešení na sčítání datových typů, které se sčítat umí. Jsem ochoten smířit se s tím, že string se v Haskellu sčítat neumí. Co třeba vektor, šlo by to? Neznám tu správnou syntaxi v Haskellu, něco takového by fungovalo?
Umí se sčítat všechny typy, co jsou Num. Základní seznam je tady: http://hackage.haskell.org/package/base-4.8.2.0/docs/Prelude.html#t:Num (http://hackage.haskell.org/package/base-4.8.2.0/docs/Prelude.html#t:Num)
Pak existuje forma "sčítání" Monoid (operátor <> z Data.Monoid). Ten je základně definovaný pro tyhle typy: http://hackage.haskell.org/package/base-4.8.2.0/docs/Data-Monoid.html (http://hackage.haskell.org/package/base-4.8.2.0/docs/Data-Monoid.html), je definovaný i pro čísla, ale přes newtype Sum a Product, protože obojí je Monoid a je potřeba si zvolit.
Oni haskellisti když definují typeclassy, tak typicky vymýšlejí nějaké zákony, které by ta TypeClass měla splňovat, aby se na to dalo spolehnout a ty funkce, které závisí např. na "Num" dávaly rozumné výsledky pro jakýkoliv typ Num, který do ní vleze. Což pro string úplně nefunguje (co String krát String?). Takže na práci se stringama mají jinou typeclass. Ale není problém si definovat vlastní typeclass s vlastníma funkcema třeba takhle:
class Scitej a where
.+. :: a -> a -> a
instance Scitej Int where
.+. = +
instance Scitej String where
.+. = ++
incrementTVar :: Scitej a => TVar a -> a -> STM ()
incrementTVar v k = atomically (modifyTVar' v (.+. k))
Nepleteš se, retry to neumí, ale zkus přijmout, že retry i případný timeout je naprosto minoritní funkcionalita, kterou si snadno doděláš.
No úplně minoritní není, protože prakticky jediný způsob, jak to dodělat, je udělat nějakou condition variable, která globálně při výstupu z transakce pošle signál.
Další věc je, že v Haskellu jediný způsob, jak přistupovat k těmhe proměnným, je v "atomically" bloku. V C++ nikoliv. Takže když ti někdo v kódu změní ta data mimo "transakci", tak hodně štěstí s hledáním chyb...
import Data.Vector
main = do
let v1 = fromList [0, 0]
let v2 = fromList [1, 1]
v0 <- newTVarIO v1 :: IO (TVar Data.Vector)
p0 <- async (tVarIncrementingProcess v0 v2)
wait p0
Tak otázka je, co myslíte tady pod pojmem sčítání. Vector má Monoid (tzn. <>, aneb append). Ale mohl byste si to definovat třeba jako:
instance Scitej a => Scitej (Vector a) where
.+. = Data.Vector.zipWith (+)
A teď to bude fungovat na vektor s jakýmkoliv obsahem, jehož obsah je instancí Scitej.
-
C++ mi z toho vychází líp, nemusím takové opičárny dělat, stačí aby se ten objekt uměl sčítat
Tak jen tak ze srandy - vector je v haskellu typ, který může obsahovat cokoliv (ne jen čísla). Něco jako vector v C++. Vector v C++ se IMO neumí standardně sčítat. Jak složité bude donutit vector v C++, aby se uměl sčítat, pokud se jeho prvky umí sčítat?
-
pro použití funcke (+) musíte mít nadefinovanou pro daný type třídu Num (ve skutečnosti nemusíte, ale slušní lidi to tak dělají)
já jsem ji pro vás napsal pro String :D
{-# LANGUAGE FlexibleInstances #-}
instance Num [Char] where
(+) = (++)
(*) _ _ = error "na tohle je už dneska moc hodin"
abs = id
signum _ = ":-)"
fromInteger = show
negate = reverse
asi to není problém pro žádný typ, ale někdy to moc nedává smysl
To je to řešení, které jsem hledal, díky. Připadá mi to teda trochu jako hack, ale budiž, když to bude fungovat... C++ mi z toho vychází líp, nemusím takové opičárny dělat, stačí aby se ten objekt uměl sčítat. Třeba pro vektor bez obalovací classy, která se nelíbila:
#include <string>
#include <thread>
#include <iostream>
#include <boost/numeric/ublas/vector.hpp>
template<typename T>
void updateThread(T& base, const T& value)
{
for (int i = 0; i < 100; ++i) {
__transaction_relaxed { base += value; }
}
}
int main()
{
boost::numeric::ublas::vector<double> v1(2);
boost::numeric::ublas::vector<double> v2(2);
std::thread t1([&]{updateThread(v1, v2);});
std::thread t2([&]{updateThread(v1, v2);});
t1.join();
t2.join();
return 0;
}
Líbí se mi to víc, než definovat Num v Haskellu.
ale ten můj prvotní kód je pro typy co se dají sčítat!
naopak ten váš kód není, vždyť pro sčítání platí a+b=b+a, platí to u toho vašeho?
-
No úplně minoritní není, protože prakticky jediný způsob, jak to dodělat, je udělat nějakou condition variable, která globálně při výstupu z transakce pošle signál.
Ne, jde to udělat i bez condition variable, stačí počkat nějaký čas a potom to zkusit znovu. Pokud mi nevadí, že na nějakou dobu uspím thread, tak je to řešení.
Další věc je, že v Haskellu jediný způsob, jak přistupovat k těmhe proměnným, je v "atomically" bloku. V C++ nikoliv. Takže když ti někdo v kódu změní ta data mimo "transakci", tak hodně štěstí s hledáním chyb...
Když ty data schovám do nějaké třídy, tak je zvenku nikdo nezmění.
Tak otázka je, co myslíte tady pod pojmem sčítání. Vector má Monoid (tzn. <>, aneb append). Ale mohl byste si to definovat třeba jako:
Myslel jsem sčítání normálního matematického vektoru: https://en.wikipedia.org/wiki/Euclidean_vector#Addition_and_subtraction
-
ale ten můj prvotní kód je pro typy co se dají sčítat!
naopak ten váš kód není, vždyť pro sčítání platí a+b=b+a, platí to u toho vašeho?
Aha, rigidní matematik... Doporučuju nechat veřejně zostudit všechny tvůrce programovacích jazyků, kteří si dovolili pro operaci spojení řetězců použit operátor +, protože se těžce provili proti a+b=b+a.
-
ale ten můj prvotní kód je pro typy co se dají sčítat!
naopak ten váš kód není, vždyť pro sčítání platí a+b=b+a, platí to u toho vašeho?
Aha, rigidní matematik... Doporučuju nechat veřejně zostudit všechny tvůrce programovacích jazyků, kteří si dovolili pro operaci spojení řetězců použit operátor +, protože se těžce provili proti a+b=b+a.
ne, pragmatický pogramátor, živící se c++ a bavící se haskellem, celou dobu mluvíte o sčítání a zároveň do toho taháte řetězce, v tom je těžké se vyznat
-
ne, pragmatický pogramátor, živící se c++ a bavící se haskellem, celou dobu mluvíte o sčítání a zároveň do toho taháte řetězce, v tom je těžké se vyznat
Uznávám, je to velmi těžké, zvlášť když 90% programovacích jazyků používá pro spojení řetězců operátor +. Mě nekamenujte, já jsem ten zmatek nezavedl, jenom ho dál udržuju :).
-
ale ten můj prvotní kód je pro typy co se dají sčítat!
naopak ten váš kód není, vždyť pro sčítání platí a+b=b+a, platí to u toho vašeho?
Aha, rigidní matematik... Doporučuju nechat veřejně zostudit všechny tvůrce programovacích jazyků, kteří si dovolili pro operaci spojení řetězců použit operátor +, protože se těžce provili proti a+b=b+a.
Jenže haskellisti to takhle mají rádi, protože na rozdíl od lidí v C++ ty typeclassy masivně používají. A ke každé typeclass je dobrým zvykem vymyslet pravidla, která by ta typeclass měla splňovat. Aby nebyl v kódu bordel. Takže když pak implementuješ něco, co má na vstupu "Num a", tak prostě neřešíš, jestli je sčítání komutativní, prostě to to je jeden z předpokladů, aby to bylo Num a. U Monoidu se třeba předpokládá, že mu nebude vadit různá asociativita. Takže opět, když budeš vytvářet kód s Monoidem, tak to prostě bude fungovat na všechny Monoidy. I na ty, na které jsi nemyslel.
Ale teď jsem si vzpomněl na jeden jednoduchý příklad něčeho, co IMO v C++ jednoduše nevyjádříš, přitom je to Haskell98. Máš záznam Osoba s atributy třeba Věk, Plat, Jméno. Chceš je setřídit sestupně podle věku, vzestupně podle platu a jména v tomto pořadí. Haskellový kód:
setrid = sortBy (flip (comparing vek) <> comparing plat <> comparing jmeno)A teď to zkus v C++....
-
Když ty data schovám do nějaké třídy, tak je zvenku nikdo nezmění.
#include <iostream>
class A {
public:
int get_x() { return x; }
A() : x(0) {}
private:
int x;
};
int main () {
A a;
*(int*)&a = 666;
std::cout << a.get_x() << std::endl;
return 0;
}
-
Ale teď jsem si vzpomněl na jeden jednoduchý příklad něčeho, co IMO v C++ jednoduše nevyjádříš, přitom je to Haskell98. Máš záznam Osoba s atributy třeba Věk, Plat, Jméno. Chceš je setřídit sestupně podle věku, vzestupně podle platu a jména v tomto pořadí. Haskellový kód:
setrid = sortBy (flip (comparing vek) <> comparing plat <> comparing jmeno)A teď to zkus v C++....
tohle je hezké, já jsem to naposledy použil pro plánování úloh (doba čekání <> priorita), taky je Monoid definovaný pro STM akce, příklad z praxe: dorazily nové data ze socketu <> timeout vypršel
-
#include <iostream>
class A {
public:
int get_x() { return x; }
A() : x(0) {}
private:
int x;
};
int main () {
A a;
*(int*)&a = 666;
std::cout << a.get_x() << std::endl;
return 0;
}
Dneska je to tady opravdu vtipné. Doporučuju zkusit taky:
cat /dev/urandom > /dev/mem
A užívat si side effecty v pure Haskellu ;)
-
*(int*)&a = 666;
}
Jenže to je asi tak na úrovni unsafePerformIO, takže celkem argument o ničem. Spíš je to o tom, že pokud v C++ programátor neudělá někde nějaký kix, a hodně dobře si to navrhne, tak to STM fungovat bude. Když dobře pořeší locky, pořadí zamykání apod., tak mu to fungovat bude. No a v Haskellu to prostě funguje a pokud se programátor omylem pokusí k něčemu přistoupit nekorektně, tak mu to compiler řekne. Já jsem poslední dobou línej a nějak když nemusím, tak tyhle věci nechám na řešení počítač.
Mimochodem, já jsem jeden projekt komplet přepsal z Haskellu do C++ - protože to běží na pomalých počítačích a bylo potřeba z toho vymáčknout veškerý možný výkon. Ale mám tam globální zámky a pevně doufám, že je nikde nezapomínám zamknout. Zlatý haskell...
-
ale ten můj prvotní kód je pro typy co se dají sčítat!
naopak ten váš kód není, vždyť pro sčítání platí a+b=b+a, platí to u toho vašeho?
Aha, rigidní matematik... Doporučuju nechat veřejně zostudit všechny tvůrce programovacích jazyků, kteří si dovolili pro operaci spojení řetězců použit operátor +, protože se těžce provili proti a+b=b+a.
ne, pragmatický pogramátor, živící se c++ a bavící se haskellem, celou dobu mluvíte o sčítání a zároveň do toho taháte řetězce, v tom je těžké se vyznat
Obecně přece a+b==b+a neplatí.
-
*(int*)&a = 666;
}
Jenže to je asi tak na úrovni unsafePerformIO, takže celkem argument o ničem. Spíš je to o tom, že pokud v C++ programátor neudělá někde nějaký kix, a hodně dobře si to navrhne, tak to STM fungovat bude. Když dobře pořeší locky, pořadí zamykání apod., tak mu to fungovat bude. No a v Haskellu to prostě funguje a pokud se programátor omylem pokusí k něčemu přistoupit nekorektně, tak mu to compiler řekne. Já jsem poslední dobou línej a nějak když nemusím, tak tyhle věci nechám na řešení počítač.
Mimochodem, já jsem jeden projekt komplet přepsal z Haskellu do C++ - protože to běží na pomalých počítačích a bylo potřeba z toho vymáčknout veškerý možný výkon. Ale mám tam globální zámky a pevně doufám, že je nikde nezapomínám zamknout. Zlatý haskell...
jo a ne, protože safe haskell, ale v c++ co?
pokud bych potřeboval veškerý dostupný výkon, tak o haskellu nebudu ani uvažovat
-
pro použití funcke (+) musíte mít nadefinovanou pro daný type třídu Num (ve skutečnosti nemusíte, ale slušní lidi to tak dělají)
já jsem ji pro vás napsal pro String :D
{-# LANGUAGE FlexibleInstances #-}
instance Num [Char] where
(+) = (++)
(*) _ _ = error "na tohle je už dneska moc hodin"
abs = id
signum _ = ":-)"
fromInteger = show
negate = reverse
asi to není problém pro žádný typ, ale někdy to moc nedává smysl
To je to řešení, které jsem hledal, díky. Připadá mi to teda trochu jako hack, ale budiž, když to bude fungovat... C++ mi z toho vychází líp, nemusím takové opičárny dělat, stačí aby se ten objekt uměl sčítat. Třeba pro vektor bez obalovací classy, která se nelíbila:
Logická chyba: U Haskellu si stěžuješ, že nelze sčítat objekt, který sčítat neumí, zatímco v C++ si pochvaluješ, že lze sčítat objekt, který se sčítat umí.
-
To je to řešení, které jsem hledal, díky. Připadá mi to teda trochu jako hack
Protože to JE hack. Je to způsob, jak se tvářit, že string má vlastnosti, které nemá*. Nikdo rozumný by to imho v praxi takhle neudělal. Podle mě je správný ten druhý způsob - nadefinovat si třídu s potřebnými funkcemi (class Scitej) a vytvořit implementaci pro všechny typy, které chci podporovat. A pak v té "inkrementační" fci pracovat s touhle třídou (incrementTVar :: Scitej a => ...).
* protože být instancí třídy znamená přesně tohle - mít nějaké vlastnosti. A některé vlastnosti Num prostě pro string neplatí, tak nemá smysl ho tam rvát.
Aha, rigidní matematik... Doporučuju nechat veřejně zostudit všechny tvůrce programovacích jazyků, kteří si dovolili pro operaci spojení řetězců použit operátor +, protože se těžce provili proti a+b=b+a.
Nejde o rigidní matematiku, ale o konzistentní sémantiku. Pokud nějaký operátor má jiný VÝZNAM pro typ A a jiný pro typ B, tak to není dobře. Jestli o tom pochybuješ, užij si, kam to vede, v JavaScriptu ;)
Obecně přece a+b==b+a neplatí.
Pokud "+" má označovat "běžné aritmetické sčítání", tak platí. Pokud to má označovat něco jiného, tak v principu může platit cokoli si pro to "něco jiného" vymyslíme. Jsou i tací koumáci, kteří jsou schopní operátor "+" předefinovat tak, aby dělal print a ještě si myslí, jak jsou chytří ;)
I pokud se pojem "sčítání" používá v nějakém zobecněném/přeneseném smyslu, tak se komutativita afaik bere jako základní nutná vlastnost, bez které by ten pojem ztratil svůj smysl. Viz třeba https://cs.wikipedia.org/wiki/S%C4%8D%C3%ADt%C3%A1n%C3%AD#Obecn.C3.A1_algebra a https://en.wikipedia.org/wiki/Addition#Addition_in_abstract_algebra
Jak říkal správně "v", řetězce se prostě nesčítají, řetězce se řetězí. A pokud bych je kór chtěl sčítat, tak jako vektory - tj. sčítat po prvcích ASCII hodoty. Ale to by asi nebylo k ničemu dobré ;)
-
Obecně přece a+b==b+a neplatí.
Pokud "+" má označovat "běžné aritmetické sčítání", tak platí. Pokud to má označovat něco jiného, tak v principu může platit cokoli si pro to "něco jiného" vymyslíme. Jsou i tací koumáci, kteří jsou schopní operátor "+" předefinovat tak, aby dělal print a ještě si myslí, jak jsou chytří ;)
Podstatné je to slovo "obecně". V Euklidovském prostoru samozřejmě komutativita sčítání platí.
-
Podstatné je to slovo "obecně". V Euklidovském prostoru samozřejmě komutativita sčítání platí.
Obecně pokud pro operaci x neplatí komutativita, tak dost dobře nemá smysl takovou operaci označovat symbolem "+". Máš nějaký příklad zaužívaného "+", které není komutativní? Mě fakt nenapadá (pokud teda nepočítám ty programátorské úlety).
-
Podstatné je to slovo "obecně". V Euklidovském prostoru samozřejmě komutativita sčítání platí.
Obecně pokud pro operaci x neplatí komutativita, tak dost dobře nemá smysl takovou operaci označovat symbolem "+". Máš nějaký příklad zaužívaného "+", které není komutativní? Mě fakt nenapadá (pokud teda nepočítám ty programátorské úlety).
U plus mě taky nic nenapadá, ale u součinu už ano ;)
-
Podstatné je to slovo "obecně". V Euklidovském prostoru samozřejmě komutativita sčítání platí.
Obecně pokud pro operaci x neplatí komutativita, tak dost dobře nemá smysl takovou operaci označovat symbolem "+". Máš nějaký příklad zaužívaného "+", které není komutativní? Mě fakt nenapadá (pokud teda nepočítám ty programátorské úlety).
Například sčítání dvou 2D vektorů na povrchu Země.
-
Nejde o rigidní matematiku, ale o konzistentní sémantiku. Pokud nějaký operátor má jiný VÝZNAM pro typ A a jiný pro typ B, tak to není dobře. Jestli o tom pochybuješ, užij si, kam to vede, v JavaScriptu ;)
V JS chvili delam, ale na tento konkretni problem jsem snad nenarazil. Jako jo, pretypovani je zajimave
let thisIsNumber = +"1";ale zatim to nikdy nevedlo k chybe. Nebo je rec o retezeni vs scitani? Pokud si nejsem jisty vstupnimi daty, tak je pretypuji/parsuji, pokud si vstupem jisty jsem (drtiva vetsina kodu), tak nemusim delat zadne opicky. Jako jiste, parkrat jsem zapomnel poresit typ z parametru v URL a to jsem se pak divil, protoze
"0" === 0jaksi nefunguje. Nevim, mozna je to i tim, ze + pro retezeni pouzivam minimalne, protoze davam prednost template strings (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) (zvyk ze Scaly):
let text = `${name}: ${value}`;Plus obcas pouziji i _.template (https://lodash.com/docs#template) a tam take neni problem s nejednoznacnym +.
a = _.template('x<%=a%><%=b%>y');
a({a:1, b:1}); // "x11y"
-
Například sčítání dvou 2D vektorů na povrchu Země.
To je pořád komutativní, ne?
U plus mě taky nic nenapadá, ale u součinu už ano ;)
U součinu jo, třeba násobení matic žejo. Ale to beztak vzniklo tak, že nějakýho poťapanýho matematika nenapadlo nic lepšího, než pro něj použít stejnej symbol a pak už se s t tím nic nedalo dělat. Matematici vůbec se symboly pracují dost volně a kreativně ;) Jako člověka odchovanýho programováním mě v matice vždycky rozčilovalo, jak se občas někde vypustí nějakej index, protože "všichni ví, že se tím myslí ...." :)
-
V JS chvili delam, ale na tento konkretni problem jsem snad nenarazil.
Třeba operátor == se chová extrémně nepředvídatelně. Ale těch haluzí je tam víc. Skoro všechno z http://charlieharvey.org.uk/page/javascript_the_weird_parts je případ nekonzistentní sémantiky.
-
Například sčítání dvou 2D vektorů na povrchu Země.
To je pořád komutativní, ne?
Na kouli jo. Na necem jinem od oka uz byt nemusi (predstav si trojuhelnik scitani vektoru na kouli s propasti/dolikem. Kdyz jedna z cest jde skrz dolik a akternativa ne, tak se to, alespon rika moje intuice, nenascita.)
-
Na kouli jo. Na necem jinem od oka uz byt nemusi (predstav si trojuhelnik scitani vektoru na kouli s propasti/dolikem. Kdyz jedna z cest jde skrz dolik a akternativa ne, tak se to, alespon rika moje intuice, nenascita.)
K prostoru s ďolíky jsem se v matice nedostal, tak nevím ;) Intuitivně mi přijde, že v prostoru, který je sem tam "zhuštěný" nebude platit skoro nic :)
-
V JS chvili delam, ale na tento konkretni problem jsem snad nenarazil.
Třeba operátor == se chová extrémně nepředvídatelně. Ale těch haluzí je tam víc. Skoro všechno z http://charlieharvey.org.uk/page/javascript_the_weird_parts je případ nekonzistentní sémantiky.
A neni nahodou good practise ho nepouzivat? Napr. na projektu, na kterem ted pracuji, je nastaveny linter tak, aby pri prekladu vyhazoval == jako warning.
-
A neni nahodou good practise ho nepouzivat? Napr. na projektu, na kterem ted pracuji, je nastaveny linter tak, aby pri prekladu vyhazoval == jako warning.
Asi jo, ale to je právě proto, že má sémantiku doprasenou až k úplné nepoužitelnosti. Nicméně ani s === nemáš vyhráno:
js> true+true===2
true
js> true-true===0
true
js>true===1
false
(ze stejného článku)
-
Například sčítání dvou 2D vektorů na povrchu Země.
To je pořád komutativní, ne?
Na kouli jo. Na necem jinem od oka uz byt nemusi (predstav si trojuhelnik scitani vektoru na kouli s propasti/dolikem. Kdyz jedna z cest jde skrz dolik a akternativa ne, tak se to, alespon rika moje intuice, nenascita.)
zkuste místo pravítka použít krejčovský metr
-
Na kouli jo. Na necem jinem od oka uz byt nemusi (predstav si trojuhelnik scitani vektoru na kouli s propasti/dolikem. Kdyz jedna z cest jde skrz dolik a akternativa ne, tak se to, alespon rika moje intuice, nenascita.)
K prostoru s ďolíky jsem se v matice nedostal, tak nevím ;) Intuitivně mi přijde, že v prostoru, který je sem tam "zhuštěný" nebude platit skoro nic :)
Proto jsem psal "obecně". Později jsem dodal, že v Euklidovském prostoru (který je podmnožinou obecného) komutativita sčítání platí.
Pokud stojíš na rovníku a vydáš se nejprve 5000 km na sever a pak 5000 km na východ, tak se dostaneš do jiného místa, než když jdeš nejprve 5000 km na východ a pak teprve 5000 km na sever.
Když budeš mít 3D prostor zakřivený přítomností hmoty, tak to také nebude komutativní. Ale to už je o něco hůře představitelné, proto jsem uvedl příklad se Zemí, kde jsem jeden rozměr ubral.
-
Pokud stojíš na rovníku a vydáš se nejprve 5000 km na sever a pak 5000 km na východ, tak se dostaneš do jiného místa, než když jdeš nejprve 5000 km na východ a pak teprve 5000 km na sever.
Máš pravdu, to jsem si neuvědomil, že jakmile tu kouli aspoň jednou obkroužíš, tak už to nevychází. (EDIT: blbost!)
No, každopádně je otázka, jestli se dá mluvit o "zobecnění". To bysme taky museli říct třeba že "zobecněné" rovnoběžky se můžou protínat, "zobecněný trojúhelník" může mít nulový obsah a tak...
V principu si asi vždycky můžeš vymyslet nějaký haluzoidní prostor, kde něco přestane platit, takže pak by "obecně" nebyla pravda nic, ne?
-
ale ten můj prvotní kód je pro typy co se dají sčítat!
naopak ten váš kód není, vždyť pro sčítání platí a+b=b+a, platí to u toho vašeho?
Tohle je krásný příklad problému s duck-typing, to budu používat.
-
Máš pravdu, to jsem si neuvědomil, že jakmile tu kouli aspoň jednou obkroužíš, tak už to nevychází.
Jo vlastně ty myslíš i bez obkroužení. Jsem dneska nějakej mimo, zastřelte mě někdo! :)))
-
Pokud stojíš na rovníku a vydáš se nejprve 5000 km na sever a pak 5000 km na východ, tak se dostaneš do jiného místa, než když jdeš nejprve 5000 km na východ a pak teprve 5000 km na sever.
Máš pravdu, to jsem si neuvědomil, že jakmile tu kouli aspoň jednou obkroužíš, tak už to nevychází.
No, každopádně je otázka, jestli se dá mluvit o "zobecnění". To bysme taky museli říct třeba že "zobecněné" rovnoběžky se můžou protínat, "zobecněný trojúhelník" může mít nulový obsah a tak...
V principu si asi vždycky můžeš vymyslet nějaký haluzoidní prostor, kde něco přestane platit, takže pak by "obecně" nebyla pravda nic, ne?
hlavně ale interpretace toho co vektor znamená nemá vliv na sčítání vektorů, asi
-
Proto jsem psal "obecně". Později jsem dodal, že v Euklidovském prostoru (který je podmnožinou obecného) komutativita sčítání platí.
No, pokud si pamatuju dobře, tak na základce nás učili, že čtverce, trojúhelníky, a všechny tyhlencty věci platí pro Euklidovský prostor. Takže tahat do toho cokoliv jiného je provokace. No a komutativita pro plus je obecná. Že ti vychází různé výsledky podle toho, zda ji pak používáš v tom či onom prosotru je tvá chyba.
-
Například sčítání dvou 2D vektorů na povrchu Země.
To je pořád komutativní, ne?
Jasně, že to je komutativní, sčítání vektorů ostatně vůbec nezávisí na okolním prostoru, to se učí snad už na základce (kterou Kit, podle toho, jak se tu projevuje, evidentně nedokončil). Na kouli je nekomutativní skládání paralelních přenosů tečných vektorů, to ale vůbec nesouvisí se sčítaním a je to docela pokročilý koncept v diferenciální geometrii.
-
hlavně ale interpretace toho co vektor znamená nemá vliv na sčítání vektorů, asi
Já bych to asi řekl tak, že o "zobecnění" se dá mluvit tam, kde nějakou operaci aplikuju na něco jinýho za zachování všech vlastností té operace - což je taky ten princip za type classes. Když mám třeba operaci sčítání na celých číslech (+_Z) a operaci sčítání na reálných číslech (+_R), tak můžu smyslupně mluvit o tom, že +_R je zobecněním +_Z.
Jakmile ale použiju stejnej symbol v jiným kontextu, kde začne mít jiný vlastnosti, tak mi to nepřijde jako zobecnění, ale jenom prostě jako použití stejnýho symbolu pro jinou operaci.
-
U plus mě taky nic nenapadá, ale u součinu už ano ;)
U součinu jo, třeba násobení matic žejo. Ale to beztak vzniklo tak, že nějakýho poťapanýho matematika nenapadlo nic lepšího, než pro něj použít stejnej symbol a pak už se s t tím nic nedalo dělat. Matematici vůbec se symboly pracují dost volně a kreativně ;) Jako člověka odchovanýho programováním mě v matice vždycky rozčilovalo, jak se občas někde vypustí nějakej index, protože "všichni ví, že se tím myslí ...." :)
U součinu už jen skalární součin, ale zase třeba u matic je třeba přiznat, že se berou spíše jako skládání, protože matice představují funkce (transformace). Jinak u těch řetězců to taky smysl má, na konkatenaci řetězců se dá prostě dívat jako na aditivní monoid, použitý symbol je jen konvence.
Jestli tě rozčiluje vypouštění částí rovnic, tak se v zájmu vlastního zdraví vyhýbej Einsteinově sčítací notaci ;)
-
Jestli tě rozčiluje vypouštění částí rovnic, tak se v zájmu vlastního zdraví vyhýbej Einsteinově sčítací notaci ;)
Doposud se mi to dařilo a nehodlám na tom nic měnit! :))
-
Pokud stojíš na rovníku a vydáš se nejprve 5000 km na sever a pak 5000 km na východ, tak se dostaneš do jiného místa, než když jdeš nejprve 5000 km na východ a pak teprve 5000 km na sever.
Máš pravdu, to jsem si neuvědomil, že jakmile tu kouli aspoň jednou obkroužíš, tak už to nevychází. (EDIT: blbost!)
No, každopádně je otázka, jestli se dá mluvit o "zobecnění". To bysme taky museli říct třeba že "zobecněné" rovnoběžky se můžou protínat, "zobecněný trojúhelník" může mít nulový obsah a tak...
V principu si asi vždycky můžeš vymyslet nějaký haluzoidní prostor, kde něco přestane platit, takže pak by "obecně" nebyla pravda nic, ne?
Projektant, který staví dům, si může směle dovolit toto zakřivení prostoru ignorovat, geodet už ne. Astronom, který by se zakřivením prostoročasu nepočítal, by dnes už nenašel uplatnění. GPS by nefungovalo, kdyby ta krabička v ruce se zakřivením prostoročasu nepočítala.
Zvykli jsme si běžné věci počítat v Euklidovském prostoru, ve kterém platí komutativní pravidla, a je to v pořádku. Stačí však operaci sčítání přetížit například na počítání v zeměpisných souřadnicích, které už v Euklidovském prostoru nejsou, a komutativní pravidlo použít nemůžeš.
BTW: 5000 km je jen 1/8 obvodu Země, ale zakřivení 2D prostoru je v něm už snadno představitelné.
-
hlavně ale interpretace toho co vektor znamená nemá vliv na sčítání vektorů, asi
Já bych to asi řekl tak, že o "zobecnění" se dá mluvit tam, kde nějakou operaci aplikuju na něco jinýho za zachování všech vlastností té operace - což je taky ten princip za type classes. Když mám třeba operaci sčítání na celých číslech (+_Z) a operaci sčítání na reálných číslech (+_R), tak můžu smyslupně mluvit o tom, že +_R je zobecněním +_Z.
Jakmile ale použiju stejnej symbol v jiným kontextu, kde začne mít jiný vlastnosti, tak mi to nepřijde jako zobecnění, ale jenom prostě jako použití stejnýho symbolu pro jinou operaci.
A co je podle tebe rozšíření skalárního součinu na komplexní čísla? Najednou přestane být komutativní, takže "začne mít jiné vlastnosti", ale přitom je definovaný úplně stejným vzorečkem.
Jinak hranice mezi zobecněním a abuse of notation je skutečně velmi tenká, viz například výpočet vektorového součinu determinantem (nebo operátor divergence).
-
A co je podle tebe rozšíření skalárního součinu na komplexní čísla? Najednou přestane být komutativní, takže "začne mít jiné vlastnosti", ale přitom je definovaný úplně stejným vzorečkem.
Fakt nevím, musel bych teď horkotěžko lovit v hlavě všechny souvislosti, který už jsem zapomněl. Radši se do toho nebudu pouštět ;)
-
A neni nahodou good practise ho nepouzivat? Napr. na projektu, na kterem ted pracuji, je nastaveny linter tak, aby pri prekladu vyhazoval == jako warning.
Asi jo, ale to je právě proto, že má sémantiku doprasenou až k úplné nepoužitelnosti. Nicméně ani s === nemáš vyhráno:
js> true+true===2
true
js> true-true===0
true
js>true===1
false
(ze stejného článku)
Tak pokud nekdo scita nebo odcita boolean, tak mu to patri ;D. Ono ani jine jazyky nejsou o moc lepsi a muzete si to prasit jak chcete (proc ma byt vubec definovane +/- nad boolean?):
std::cout << true+true; // 2
std::cout << (true+true == true); // 0
JS ma hodne zaludnosti prave kvuli tomu automatickemu pretypovani, jenze to je IMO cena za tu dynamicnost. Ve statickych jazycich to hlida prekladac, v dynamickych programator. A bez "chytreho" pretypovani by zase kod byl plny castu, coz se myslim u dynamickych jazyku moc nenosi. Sice JS neni zadna vyhra, ale IMO PHP je na tom jeste o prapast hur (tam oproti JS nehrapruji obcas typy a nazvy metod, ale dost casto). A teda nektere veci jsou fakt lahudka:
var_dump("01" == "1"); // samebool(true)
$foo = 5 + "10 Little Piggies"; // $foo is integer (15)
PS: Moje oblibenene srovnani JS a ScalaJS:
javascript> ["10", "10", "10", "10"].map(parseInt)
[10, NaN, 2, 3] // WTF
scala.js> List("10", "10", "10", "10").map(parseInt)
List(10, 10, 10, 10) // Yay!
-
Tak pokud nekdo scita nebo odcita boolean, tak mu to patri ;D.
No a to je právě ono - operátor dělá pokaždé něco jinýho, podle toho, jaký použiju parametry. Je fakt, že u JS je tou pravou příčinou zběsilý přetypování, ale výsledek je stejnej.
-
No a to je právě ono - operátor dělá pokaždé něco jinýho, podle toho, jaký použiju parametry. Je fakt, že u JS je tou pravou příčinou zběsilý přetypování, ale výsledek je stejnej.
Jako ja chapu problem, ale kdyz tim "trpi" (neni to zamyslena vlastnost?) i takovi mamuti jako C a C++, tak bych rekl, ze je to spise standard na poli jazyku, nez neco neobvykleho, za co by jazyk mel byt zatracovan. A teda myslim si, ze u toho C je to mineno jako vlastnost/tradeoff - radeji vyssi vykon (v pripade JS asi odpadnuti nutnosti vsude pretypovavat), nez bezpecnost/typova cistota a nizsi vykon.
Osobne si typu vazim a prestoze nedelam v Haskellu, ktery zvladne odvodit skoro vse, tak porad preferuji oddeleny typ boolean a jen mnozinu operatoru, ktere davaji pro dany typ smysl.
-
Tak pokud nekdo scita nebo odcita boolean, tak mu to patri ;D. Ono ani jine jazyky nejsou o moc lepsi a muzete si to prasit jak chcete (proc ma byt vubec definovane +/- nad boolean?):
No, já bych čekal že to vyhodí výjimku: "Tato operace není pro tento typ definována."
JS ma hodne zaludnosti prave kvuli tomu automatickemu pretypovani,
Ano. Že přetypovává i to, co by se přetypovávat nemělo.
jenze to je IMO cena za tu dynamicnost.
To imho s dynamičností nesouvisí.
Ve statickych jazycich to hlida prekladac, v dynamickych programator. A bez "chytreho" pretypovani by zase kod byl plny castu, coz se myslim u dynamickych jazyku moc nenosi.
IMHO není problém v tom, že se něco automaticky přetypovává, nebo, že je to dynamické (haskell má automatický extends, a funguje to skvěle), ale v tom, že javascript ani python nejsou silně typované, takže ten automatický cast nemá informace o tom, co může a na co přetypovávat. A možná je to i tím, že v tom maj prostě bordel.
-
Jako ja chapu problem, ale kdyz tim "trpi" (neni to zamyslena vlastnost?) i takovi mamuti jako C a C++
Však taky C++ není žádný vzorově čistý jazyk :) Skoro bych řekl, že je asi tak druhý třetí nejhorší :)
ze u toho C je to mineno jako vlastnost/tradeoff - radeji vyssi vykon (v pripade JS asi odpadnuti nutnosti vsude pretypovavat), nez bezpecnost/typova cistota a nizsi vykon.
To, o čem je řeč (konzistentní chování operátorů) imho s výkonem nijak nesouvisí. Souhlasím s BoneFlute, že
1. pokud jazyk dělá typovou kontrolu, měl by vyhodit výjimku u operací, které nedávají smysl
2. se statičností/dynamičností to nesouvisí - ta jenom říká, jestli se kontrola dělá při překladu nebo až při běhu
-
Tak pokud nekdo scita nebo odcita boolean, tak mu to patri ;D.
No a to je právě ono - operátor dělá pokaždé něco jinýho, podle toho, jaký použiju parametry. Je fakt, že u JS je tou pravou příčinou zběsilý přetypování, ale výsledek je stejnej.
Tak ale sčítat boolean jde celkem dobře, stačí vzít plus z Boolovy algebry.
-
Tak ale sčítat boolean jde celkem dobře, stačí vzít plus z Boolovy algebry.
To by tam ale nesmělo být přetypování na int ani by nesměl být true definovaný jako 1 (což je myslím v C).
-
Tak ale sčítat boolean jde celkem dobře, stačí vzít plus z Boolovy algebry.
To by tam ale nesmělo být přetypování na int ani by nesměl být true definovaný jako 1 (což je myslím v C).
Jasně, v C to je jako v assembleru, jen říkám, že to jde udělat logicky, ne že to tak jazyky mají.
-
Tak ale sčítat boolean jde celkem dobře, stačí vzít plus z Boolovy algebry.
To by tam ale nesmělo být přetypování na int ani by nesměl být true definovaný jako 1 (což je myslím v C).
Jasně, v C to je jako v assembleru, jen říkám, že to jde udělat logicky, ne že to tak jazyky mají.
Ja bych rekl, ze s tim + je problem, ze se kryje s programatory vice pouzivanym a zazitym || nebo or. Jak jste psali vyse (doufam ze to bylo toto vlakno :D), IMO jde o podobny konflikt mezi IT a matematikou.
-
Tak ale sčítat boolean jde celkem dobře, stačí vzít plus z Boolovy algebry.
To by tam ale nesmělo být přetypování na int ani by nesměl být true definovaný jako 1 (což je myslím v C).
Jasně, v C to je jako v assembleru, jen říkám, že to jde udělat logicky, ne že to tak jazyky mají.
Ja bych rekl, ze s tim + je problem, ze se kryje s programatory vice pouzivanym a zazitym || nebo or. Jak jste psali vyse (doufam ze to bylo toto vlakno :D), IMO jde o podobny konflikt mezi IT a matematikou.
Samozřejmě, používání symbolů je vždy o konvenci a nejde ani tak o konflikt, jako o prostý rozdíl ve značení. Nicméně matematika tu byla dříve, takže tam, kde to jde, by si informatici neměli vymýšlet blbosti a raději převzít zavedené značení.
-
Haskell budoucnost rozhodne ma, dokazuje to i tato diskuse. Spousta diskutujicich Haskell evidentne zna. Ale asi nikdy nebude uplne mainstream.
Jinak Haskell je v podstate dnes standard pro temer jakekoli CS clanky, ktere se tykaji programovacich jazyku.
-
S Haskell jsem skončil, když nepochopil, to jsou to monády.... :-(
Takže, abych si to ujasnil...
1) Pro používání monád je nemusím chápat...
2) Haskell je pure, ale v monádách se může klidně programovat imperativně...
3) V monádách klidně můžou existovat mutable typy...
4) Ten, kdo chápe monády, je nedokáže dobře vysvětlit...
-
S Haskell jsem skončil, když nepochopil, to jsou to monády.... :-(
Takže, abych si to ujasnil...
1) Pro používání monád je nemusím chápat...
2) Haskell je pure, ale v monádách se může klidně programovat imperativně...
3) V monádách klidně můžou existovat mutable typy...
4) Ten, kdo chápe monády, je nedokáže dobře vysvětlit...
Pojem monáda je magický, lidi si ten koncept totiž neumí nijak rozumně představit, ale přitom je používají - nevědomky - ve vlastních programech. Doporučuji články na Wikipedii, je jich tam k monádám několik.
-
Takže, abych si to ujasnil...
TL;DR: ani jedno z toho, co pises, neni pravda tak, jak to pises :)
1) Pro používání monád je nemusím chápat...
Monady jsou mj. prostredek, jak v pure+lazy jazyce dosahnout souslednosti nejakych operaci. Cili je to vyborny zpusob, jak v pure+lazy jazyce psat IO. Neni to ale jediny zpusob. V principu muzes delat IO i bez znalosti monad, ale monady jsou lepsi.
2) Haskell je pure, ale v monádách se může klidně programovat imperativně...
Monady jsou zpusob, jak v pure+lazy psat imperativnim stylem (tj. psat posloupnost operaci, ktere se provedou ve stejnem poradi, jak jsem je napsal).
3) V monádách klidně můžou existovat mutable typy...
Ne.
4) Ten, kdo chápe monády, je nedokáže dobře vysvětlit...
Vetsina lidi asi ne :) Ale existuji i dobra vysvetleni. Mne se treba libi, ze Learn you a Haskell mluvni prvne o IO a az o par kapitol dal o monadach. Pokud to nepochopis z tohodle textu a za radneho snazeni se, tak asi Haskell neni vhodny jazyk pro tebe (bez jakekoliv urazky - to je proste fakt, ne kazdemu ten zpusob uvazovani musi vyhovovat).
-
... V Haskellu je pro tyto účely tzv. ST monad, který de-fakto umožňuje provádět mutable programování v "izolaci" - tzn. ta mutabilita je uvnitř a neuteče nikam ven. Mně připadá, že ale výsledný kód má do přehlednosti dost daleko - je to takové ... imperativní ...
Jinak je pravda, že jsem tomu zase tolik nedal... přeci jen, Erlang se mi zalouvá více... :)
-
... V Haskellu je pro tyto účely tzv. ST monad, který de-fakto umožňuje provádět mutable programování v "izolaci" - tzn. ta mutabilita je uvnitř a neuteče nikam ven. Mně připadá, že ale výsledný kód má do přehlednosti dost daleko - je to takové ... imperativní ...
Jinak je pravda, že jsem tomu zase tolik nedal... přeci jen, Erlang se mi zalouvá více... :)
To, co pise gl, je zavadejici. Zadna mutabilita tam neni. Stejne jako funkce s typem IO nejsou "neciste". Vsechno je porad ciste a imutabilni, akorat existuje zpusob, jak i presto dosahovat nejakych vnejsich efektu.
-
To, co pise gl, je zavadejici. Zadna mutabilita tam neni. Stejne jako funkce s typem IO nejsou "neciste". Vsechno je porad ciste a imutabilni, akorat existuje zpusob, jak i presto dosahovat nejakych vnejsich efektu.
A nebo čistě a funkcionálně říct, že chceme nějakého efektu.
Mimochodem to se týká hlavně IO operací. Funkcionálnost s pořadím imho nutně nekoliduje. Pokud tedy chápu správně, že FP je o referenční transparentnosti a pravidle, kdy funkce je ovlivňována jen argumenty. Jen je to občas problém jak funkcionálně vyjádřit následnost.
-
Funkcionálnost s pořadím imho nutně nekoliduje. Pokud tedy chápu správně, že FP je o referenční transparentnosti a pravidle, kdy funkce je ovlivňována jen argumenty. Jen je to občas problém jak funkcionálně vyjádřit následnost.
Pokud mas referencni transparentnost, tak to znamena, ze jakykoliv vyraz muzes vyhodnotit predem, ulozit si vyslednou hodnotu do tabulky a kdyz priste narazis na stejny vyraz, dosadis tam jenom hodnotu z tabulky. Takze z tehle definice pak muzes vyrazy vyhodnocovat v libovolnem poradi. A jedine, u ceho by te poradi vyhodnocovani mohlo zajimat, je IO. U vseho ostatniho je ti to putna :)
-
Jinak je pravda, že jsem tomu zase tolik nedal... přeci jen, Erlang se mi zalouvá více... :)
Hodnej hoch! Cim vic erlangistu, tim lepsi svet! :))
Nicmene mam pro tebe takovou pomucku, jak mozna to do notaci pochopis lip, hele, vubec to neni slozity:
Mas ucebnicovy priklad:
main = do
putStrLn "Hello, what's your name?"
name <- getLine
putStrLn ("Hey " ++ name ++ ", you rock!")
to "do" a "<-" je jenom syntaktickej cukr, ve skutecnosti je to zkratka pro tohle:
main =
(putStrLn "Hello, what's your name?") >>
(getLine >>= (\name -> putStrLn ("Hey " ++ name ++ ", you rock!")))
Vsimni si tam te lambdy "\name ->". Operator (>>=) dela to, ze rika "vystup prvni IO akce prived na vstup lambdy, ktera vraci druhou IO akci". A operator (>>) dela to same, akorat vystup prvni IO akce zahodi. Takze muzeme operator (>>) nahradit obecnejsim (>>=) a kod dal prevest na:
main =
(putStrLn "Hello, what's your name?") >>=
(\_ ->
getLine >>=
(\name -> putStrLn ("Hey " ++ name ++ ", you rock!"))
)
No a ted si musime jenom ujasnit, ze tenhle kod porad nic nevykonava, je to jenom staticky popis toho, jake akce by se mely provest, v jakem poradi a jaka si maji predavat data. Operator (>>=) pouziva ty divne monady:
Prelude> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
...takze se jich zbavime taky!
import GHC.Base (bindIO)
main =
bindIO
(putStrLn "Hello, what's your name?")
(\_ ->
bindIO
getLine
(\name -> putStrLn ("Hey " ++ name ++ ", you rock!"))
)
...funkce bindIO nedela nic jineho nez ze dva RECEPTY na IO (staticky popis IO akci) posklada za sebe, cimz vznikne nova IO akce zahrnujici ty dve:
Prelude GHC.Base> :t bindIO
bindIO :: IO a -> (a -> IO b) -> IO b
Jak vidis, abys delal IO v Haskellu, monady nepotrebujes. Postupovali jsme opacnym zpusobem, nez jde abstrakce: k razeni IO potrebujes otravne lambdy, takze je fajn si zavest operatory, ktere te od nich odstini. No a kdyz mas tohle, tak si jeste zavedes dalsi syntakticky cukr, ktery to jeste cely obali tak, aby to vypadalo jakoze imperativni programovani. Co to pripomina? No prece promises s JS! Protoze to jsou taky de facto monady. Akorat o tom nikdo nemluvi, nikdo z toho timpadem nema strach a vsichni to naprosto v klidu pouzivaji :)
-
Jinak je pravda, že jsem tomu zase tolik nedal... přeci jen, Erlang se mi zalouvá více... :)
Hodnej hoch! Cim vic erlangistu, tim lepsi svet! :))
Nicmene mam pro tebe takovou pomucku, jak mozna to do notaci pochopis lip, hele, vubec to neni slozity:
Mas ucebnicovy priklad:
main = do
putStrLn "Hello, what's your name?"
name <- getLine
putStrLn ("Hey " ++ name ++ ", you rock!")
to "do" a "<-" je jenom syntaktickej cukr, ve skutecnosti je to zkratka pro tohle:
main =
(putStrLn "Hello, what's your name?") >>
(getLine >>= (\name -> putStrLn ("Hey " ++ name ++ ", you rock!")))
Vsimni si tam te lambdy "\name ->". Operator (>>=) dela to, ze rika "vystup prvni IO akce prived na vstup lambdy, ktera vraci druhou IO akci". A operator (>>) dela to same, akorat vystup prvni IO akce zahodi. Takze muzeme operator (>>) nahradit obecnejsim (>>=) a kod dal prevest na:
main =
(putStrLn "Hello, what's your name?") >>=
(\_ ->
getLine >>=
(\name -> putStrLn ("Hey " ++ name ++ ", you rock!"))
)
No a ted si musime jenom ujasnit, ze tenhle kod porad nic nevykonava, je to jenom staticky popis toho, jake akce by se mely provest, v jakem poradi a jaka si maji predavat data. Operator (>>=) pouziva ty divne monady:
Prelude> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
...takze se jich zbavime taky!
import GHC.Base (bindIO)
main =
bindIO
(putStrLn "Hello, what's your name?")
(\_ ->
bindIO
getLine
(\name -> putStrLn ("Hey " ++ name ++ ", you rock!"))
)
...funkce bindIO nedela nic jineho nez ze dva RECEPTY na IO (staticky popis IO akci) posklada za sebe, cimz vznikne nova IO akce zahrnujici ty dve:
Prelude GHC.Base> :t bindIO
bindIO :: IO a -> (a -> IO b) -> IO b
Jak vidis, abys delal IO v Haskellu, monady nepotrebujes. Postupovali jsme opacnym zpusobem, nez jde abstrakce: k razeni IO potrebujes otravne lambdy, takze je fajn si zavest operatory, ktere te od nich odstini. No a kdyz mas tohle, tak si jeste zavedes dalsi syntakticky cukr, ktery to jeste cely obali tak, aby to vypadalo jakoze imperativni programovani. Co to pripomina? No prece promises s JS! Protoze to jsou taky de facto monady. Akorat o tom nikdo nemluvi, nikdo z toho timpadem nema strach a vsichni to naprosto v klidu pouzivaji :)
Nechtěl bys napsat nějaký Yet another monad tutorial?
S monádami to je jako s Rudým baronem, všichni britští piloti věděli, že je nesestřelitelný, a soubojům s ním se vyhýbali, až přišel nováček z Kanady, sotva opustivší vojenskou školu pro piloty, který o něm v životě neslyšel a při prvním letu ho sundal :)
-
... V Haskellu je pro tyto účely tzv. ST monad, který de-fakto umožňuje provádět mutable programování v "izolaci" - tzn. ta mutabilita je uvnitř a neuteče nikam ven. Mně připadá, že ale výsledný kód má do přehlednosti dost daleko - je to takové ... imperativní ...
Jinak je pravda, že jsem tomu zase tolik nedal... přeci jen, Erlang se mi zalouvá více... :)
To, co pise gl, je zavadejici. Zadna mutabilita tam neni. Stejne jako funkce s typem IO nejsou "neciste". Vsechno je porad ciste a imutabilni, akorat existuje zpusob, jak i presto dosahovat nejakych vnejsich efektu.
To jsem nepsal já. Byla to odpověď andyho na mou otázku. Pochopil jsem to tak, že mutabilita tam je. Co jiného dělá writeSTRef?
-
To jsem nepsal já. Byla to odpověď andyho na mou otázku. Pochopil jsem to tak, že mutabilita tam je. Co jiného dělá writeSTRef?
Nevim, ja s tim praktickym Haskellem nemam zkusenosti. Ale predpokladam, ze to bude neco ve smyslu retezeni lambd, kde kazda vezme hodnotu a vrati jinou (nebo stejnou). Takze "zmena hodnoty" pak znamena za ten retezec lamb pridat dalsi.
Nebo tak neco ;) Ale kazdopadne porad je to nejakej figl jak simulovat vlastnosti mutability v imutabilnim prostredi.
-
Nechtěl bys napsat nějaký Yet another monad tutorial?
Naopak - to neni navod k monadam, to je navod, jak pochopit IO v Haskellu pomoci ostentativniho ignorovani pojmu monada ;)
-
Nechtěl bys napsat nějaký Yet another monad tutorial?
Naopak - to neni navod k monadam, to je navod, jak pochopit IO v Haskellu pomoci ostentativniho ignorovani pojmu monada ;)
spíš přitroublého ignorování, bindIO není standardní haskell, ale ghc
-
spíš přitroublého ignorování, bindIO není standardní haskell, ale ghc
To na veci ale vubec nic nemeni.
-
spíš přitroublého ignorování, bindIO není standardní haskell, ale ghc
To na veci ale vubec nic nemeni.
nepřijde vám jako problém, že váš "tutoriál" k IO v haskellu nefunguje se standardním interpretrem haskellu?
-
To jsem nepsal já. Byla to odpověď andyho na mou otázku. Pochopil jsem to tak, že mutabilita tam je. Co jiného dělá writeSTRef?
Nevim, ja s tim praktickym Haskellem nemam zkusenosti. Ale predpokladam, ze to bude neco ve smyslu retezeni lambd, kde kazda vezme hodnotu a vrati jinou (nebo stejnou). Takze "zmena hodnoty" pak znamena za ten retezec lamb pridat dalsi.
Nebo tak neco ;) Ale kazdopadne porad je to nejakej figl jak simulovat vlastnosti mutability v imutabilnim prostredi.
Ta nová hodnota se zapíše na stejné místo v paměti. Jinak by používání ST nedávalo smysl.
-
nepřijde vám jako problém, že váš "tutoriál" k IO v haskellu nefunguje se standardním interpretrem haskellu?
Ne, protoze bindovani IO je operace, kterou uzivatel nepotrebuje (prave diky monadam), takze ani ve specifikaci nemusi byt. Ale jelikoz v GHC je, da se snadno ukazat, jak IO doopravdy funguje - ze to neni zadna tajemna magie, ale jenom takovy trosku lepsi seznam IO operaci, ktery se provede jenom proto, ze se posle do main.
Ta nová hodnota se zapíše na stejné místo v paměti.
Ne. Operace "zapis neco do pameti" v cistem jazyce vubec nema co pohledavat ;)
-
nepřijde vám jako problém, že váš "tutoriál" k IO v haskellu nefunguje se standardním interpretrem haskellu?
Ne, protoze bindovani IO je operace, kterou uzivatel nepotrebuje (prave diky monadam), takze ani ve specifikaci nemusi byt. Ale jelikoz v GHC je, da se snadno ukazat, jak IO doopravdy funguje - ze to neni zadna tajemna magie, ale jenom takovy trosku lepsi seznam IO operaci, ktery se provede jenom proto, ze se posle do main.
Ta nová hodnota se zapíše na stejné místo v paměti.
Ne. Operace "zapis neco do pameti" v cistem jazyce vubec nema co pohledavat ;)
TL;DR ne
-
Ne. Operace "zapis neco do pameti" v cistem jazyce vubec nema co pohledavat ;)
ST je to samé, co IO omezený na IORefy uvnitř runST a typovým systémem ošéfované tak, že výsledek může být pure. V ST v podstatě existují akce "newSTRef", což je "efekt", který "někde" naalokuje místo, a potom "readSTRef" a "writeSTRef", které tam "někam" zapíše nebo přečte. Jsou to "ST akce" - podobně jako IO akce, a v ST monadu se řetězí. No a protože ST v RealWorld nic nemění, tak je to typově možné schovat za něco typu unsafePerformIO.
Výsledený program v ST je de-fakto imperativní - obzvlášť, když si to napíšeš v do blocku. A nikdyjsem to ještě nepoužil, protože implementaci nějakých výpočetních algoritmů nedělám úplně často.
-
Jsou to "ST akce" - podobně jako IO akce, a v ST monadu se řetězí.
Však to je přesně to, co jsem psal:
to bude neco ve smyslu retezeni lambd
- pokud bys tam měl dostupnou operaci odpovídající bindIO, tak by to ani monáda být nemusela.
-
TL;DR ne
A nechceš to nějak vyargumentovat?
-
TL;DR ne
A nechceš to nějak vyargumentovat?
stref si můžete nastudovat sám a argument s bindio je asi jako říct, že nepotřebujete městskou hromadnou dopravu, protože se můžete svézt tramvají (viz haskell 2010 report a implementace instance Monad IO v ghc)
-
Ne. Operace "zapis neco do pameti" v cistem jazyce vubec nema co pohledavat ;)
ST je to samé, co IO omezený na IORefy uvnitř runST a typovým systémem ošéfované tak, že výsledek může být pure. V ST v podstatě existují akce "newSTRef", což je "efekt", který "někde" naalokuje místo, a potom "readSTRef" a "writeSTRef", které tam "někam" zapíše nebo přečte. Jsou to "ST akce" - podobně jako IO akce, a v ST monadu se řetězí. No a protože ST v RealWorld nic nemění, tak je to typově možné schovat za něco typu unsafePerformIO.
Výsledený program v ST je de-fakto imperativní - obzvlášť, když si to napíšeš v do blocku. A nikdyjsem to ještě nepoužil, protože implementaci nějakých výpočetních algoritmů nedělám úplně často.
např. https://gist.github.com/dmatveev/5993223
-
stref si můžete nastudovat sám
Prvně bych musel vědět, v čem se mýlím, abych věděl, co tam mám hledat. Zatím se mi to jeví jako obdoba http://elixir-lang.org/docs/stable/elixir/Agent.html#update/3 - s tím rozdílem, že v Haskellu je to lazy, takže pokud dlouho píšu a nečtu, tak se ty lambdy kupí a přeteče zásobník.
a argument s bindio je asi jako říct, že nepotřebujete městskou hromadnou dopravu, protože se můžete svézt tramvají (viz haskell 2010 report a implementace instance Monad IO v ghc)
Mám obavu, že ten argument nechápete. Prostě IO v Haskellu je totéž, jako by to byl string "načti_ze_stdio", "zapiš_do_souboru", ... a tyhle statické, symbolické popisy IO akcí by se řadily za sebe třeba do Listu. Monády jsou to samé, akorát mi umožňují předávat mezi IO akcemi libovolná data a checkovat jejich typy. Nic víc, jenom prostě řazení IO operací. Plus možnost failnout. A bindIO dělá to samé, akorát ne obecně pro jakoukoli monádu, ale jenom specificky pro IO akce. Takže (>>=) není nic jiného než zobecnění bindIO na jakoukoli monádu. (EDIT: z čehož právě plyne, že když specifikace obsahuje Monad, tak nepotřebuje explicitní bindIO, může ho schovat v implementaci, což je přesně to, co jsem řekl a nevidím tam nic nepravdivého)
-
Mirek Prýmek děkuji za vysvětlení mondád.
to s tím pořadím tak nějak pobírám... ale například výpis na obrazovku, je side efekt, nebo se mýlím?
a jinak, k otázce budoucnosti... další pohled: Živím se jako programátor, naučím se Haskell... bude mi to v práci k něčemu? V současné době jsem dostal firemní PC s windows, tak se chci naučit .NET... protože ta znalost mě může jednoho dne živit... ale Haskell? Krom takového toho domácího žvýkání...
Jinak Erlang byl ten AHA jazyk, kde jsem skutečně viděl, že se dá ve funkcionálním programování skutečně programovat... že to není jenom peklo s rekurzemi a dalšími úchylkami...
-
to s tím pořadím tak nějak pobírám... ale například výpis na obrazovku, je side efekt, nebo se mýlím?
Je. Proto taky
Prelude> :t putStrLn
putStrLn :: String -> IO ()
a jinak, k otázce budoucnosti... další pohled: Živím se jako programátor, naučím se Haskell... bude mi to v práci k něčemu? V současné době jsem dostal firemní PC s windows, tak se chci naučit .NET... protože ta znalost mě může jednoho dne živit... ale Haskell? Krom takového toho domácího žvýkání...
Existují firmy, kde se Haskell používá, ale moc jich nebude. Spíš je to imho jazyk, který programátorovi umožní uvědomit si, co je vlastně co a jak se dají některé věci řešit trochu jinak... Třeba to všudypřítomné skládání funkcí je dost inspirativní, dá se použít v jakémkoli jazyce.
Jinak Erlang byl ten AHA jazyk, kde jsem skutečně viděl, že se dá ve funkcionálním programování skutečně programovat... že to není jenom peklo s rekurzemi a dalšími úchylkami...
Jj, měl jsem to podobně. Zkus ještě Elixir, ten je imho ještě lepší a s Erlangem plně kompatibilní, takže o nic nepřijdeš.
-
stref si můžete nastudovat sám
Prvně bych musel vědět, v čem se mýlím, abych věděl, co tam mám hledat. Zatím se mi to jeví jako obdoba http://elixir-lang.org/docs/stable/elixir/Agent.html#update/3 - s tím rozdílem, že v Haskellu je to lazy, takže pokud dlouho píšu a nečtu, tak se ty lambdy kupí a přeteče zásobník.
Řetězení ST akcí znamená, že se provádí sekvenčně uvnitř runST bloku. Tak jsem pochopil Andyho odpověď já. Na žádný zásobník se nic neukládá. Když STRef přepíšete, tak se předchozí hodnota ztratí. Funguje to stejně jako proměnná v C.
-
Řetězení ST akcí znamená, že se provádí sekvenčně uvnitř runST bloku. Tak jsem pochopil Andyho odpověď já. Na žádný zásobník se nic neukládá. Když STRef přepíšete, tak se předchozí hodnota ztratí. Funguje to stejně jako proměnná v C.
To zjevně není pravda:
Be warned that modifySTRef does not apply the function strictly. This means if the program calls modifySTRef many times, but seldomly uses the value, thunks will pile up in memory resulting in a space leak. This is a common mistake made when using an STRef as a counter. For example, the following will leak memory and likely produce a stack overflow:
https://hackage.haskell.org/package/base-4.8.2.0/docs/Data-STRef.html#v:modifySTRef
Tenhle problém není u writeSTRef, protože ten předchozí stav (řetězec thunků) zahodí, takže nic nebobtná.
Z toho mi přijde, že to je zjevně řetězec lambd (thunků). EDIT: jakože řetězec modifikací stavu.
-
Prostě IO v Haskellu je totéž, jako by to byl string "načti_ze_stdio", "zapiš_do_souboru", ... a tyhle statické, symbolické popisy IO akcí by se řadily za sebe třeba do Listu. Monády jsou to samé, akorát mi umožňují předávat mezi IO akcemi libovolná data a checkovat jejich typy. Nic víc, jenom prostě řazení IO operací. Plus možnost failnout. A bindIO dělá to samé, akorát ne obecně pro jakoukoli monádu, ale jenom specificky pro IO akce. Takže (>>=) není nic jiného než zobecnění bindIO na jakoukoli monádu. (EDIT: z čehož právě plyne, že když specifikace obsahuje Monad, tak nepotřebuje explicitní bindIO, může ho schovat v implementaci, což je přesně to, co jsem řekl a nevidím tam nic nepravdivého)
Dají se tedy monády přirovnat např. k pojmenovaným rourám s definovanou vnitřní strukturou?
-
Živím se jako programátor, naučím se Haskell... bude mi to v práci k něčemu?
Otázkou je, k čemu přesně by to mělo být. Řada věcí, co Haskell nabízí, je v jiných jazycích řešena lépe a jednodušeji než v (GHC) Haskellu.
Například, pokud si chcete pohrát se silnějším typovým systémem, můžete zkusit Idris (BTW Idris má lepší mechanismus pro sledování efektů než Haskell). Pokud chcete další možnosti, jak řídit běh programu, můžete zkusit jazyk s podporou delimited continuations (např. vhodný dialekt Scheme nebo v omezené formě ocaml-multicore nebo OCaml s knihovnou delimcc), pokud chcete paralelismus, zkuste Clojure nebo Manticore.
V současné době jsem dostal firemní PC s windows, tak se chci naučit .NET...
Pokud se chcete učit .NET, tak možná bude mít smysl kouknout se na F#, až se naučíte C#.
-
Dají se tedy monády přirovnat např. k pojmenovaným rourám s definovanou vnitřní strukturou?
Přesně tak - to jsou právě ty koleje, o kterých jsem mluvil nedávno. A hezký je na tom to, že je zabezpečeno, že spojit jdou jenom koleje, který na sebe pasují (předchozí fce vrací IO akci stejného typu jako následující lambda bere jako vstup).
-
To zjevně není pravda:
Be warned that modifySTRef does not apply the function strictly. This means if the program calls modifySTRef many times, but seldomly uses the value, thunks will pile up in memory resulting in a space leak. This is a common mistake made when using an STRef as a counter. For example, the following will leak memory and likely produce a stack overflow:
https://hackage.haskell.org/package/base-4.8.2.0/docs/Data-STRef.html#v:modifySTRef
Tenhle problém není u writeSTRef, protože ten předchozí stav (řetězec thunků) zahodí, takže nic nebobtná.
Z toho mi přijde, že to je zjevně řetězec lambd (thunků). EDIT: jakože řetězec modifikací stavu.
Já ti teď vůbec nerozumím. Řetězení lambd v monadu chápu jako řetězení ST () akcí. To se neděje. Vykonává se to stejně jako IO monad, dokonce vevnitř to je snad nějaký hodně podobný (akorát místo RealWorld tam je něco jiného). Co se může stát je, že STRef může obsahovat thunk (asi... nikdy jsem s tím nedělal), takže pokud to neforcneš, tak si v tom můžeš ten lazy pure výpočet nařetězit. Ale to nemá s ST nic společného. Ten 'zápis (toho thunku) do ST' se nikde neřetězí, ten se provede.
-
Dají se tedy monády přirovnat např. k pojmenovaným rourám s definovanou vnitřní strukturou?
Tak ten vtip zní "A monad is just a monoid in the category of endofunctors". Tak prozatím to chápu tak, že "monoid" znamená v podstatě "list" nebo "stream" a "kategorie endofunktorů" znamená, že jsem z té akce v listu schopen vytáhnout hodnotu a nějak ji předat následujícím akcím..
-
Tak ten vtip zní "A monad is just a monoid in the category of endofunctors". Tak prozatím to chápu tak, že "monoid" znamená v podstatě "list" nebo "stream" a "kategorie endofunktorů" znamená, že jsem z té akce v listu schopen vytáhnout hodnotu a nějak ji předat následujícím akcím..
Tak to chápeš blbě :) Monoid znamená, že pro tu věc platí pravidla monoidu (asociativita a existence neutrálního prvku) a endofunktor je funktor, který mapuje kategorii na sebe samu.
Endofunktor je to proto, že libovolný haskellovský typ mapuje opět na haskellovský typ. A monoid je to proto, že bind je asociativní a return je neutrální prvek.
...ale neřekl bych, že by komukoli "normálnímu" tahle definice pomohla pochopit, o co jde :)
-
Já ti teď vůbec nerozumím. Řetězení lambd v monadu chápu jako řetězení ST () akcí. To se neděje. Vykonává se to stejně jako IO monad, dokonce vevnitř to je snad nějaký hodně podobný (akorát místo RealWorld tam je něco jiného). Co se může stát je, že STRef může obsahovat thunk (asi... nikdy jsem s tím nedělal), takže pokud to neforcneš, tak si v tom můžeš ten lazy pure výpočet nařetězit. Ale to nemá s ST nic společného. Ten 'zápis (toho thunku) do ST' se nikde neřetězí, ten se provede.
A proč teda podle tebe v tom příkladu dojde ke stack overflow?
-
Řetězení ST akcí znamená, že se provádí sekvenčně uvnitř runST bloku. Tak jsem pochopil Andyho odpověď já. Na žádný zásobník se nic neukládá. Když STRef přepíšete, tak se předchozí hodnota ztratí. Funguje to stejně jako proměnná v C.
To zjevně není pravda:
Be warned that modifySTRef does not apply the function strictly. This means if the program calls modifySTRef many times, but seldomly uses the value, thunks will pile up in memory resulting in a space leak. This is a common mistake made when using an STRef as a counter. For example, the following will leak memory and likely produce a stack overflow:
https://hackage.haskell.org/package/base-4.8.2.0/docs/Data-STRef.html#v:modifySTRef
Tenhle problém není u writeSTRef, protože ten předchozí stav (řetězec thunků) zahodí, takže nic nebobtná.
Z toho mi přijde, že to je zjevně řetězec lambd (thunků). EDIT: jakože řetězec modifikací stavu.
Máte pravdu. To jsem přehlédl. Předtím se mluvilo o writeSTRef. Jde tam jen o líné vyhodnocení argumentu. V dokumentaci se doporučuje použít modifySTRef'.
-
a jinak, k otázce budoucnosti... další pohled: Živím se jako programátor, naučím se Haskell... bude mi to v práci k něčemu? V současné době jsem dostal firemní PC s windows, tak se chci naučit .NET... protože ta znalost mě může jednoho dne živit... ale Haskell? Krom takového toho domácího žvýkání...
No já jsem pochopil, co to znamená typový systém. Ano, některé jiné jazyky mají ještě lepší, ale ten v haskellu je úplně někdy jinde než cokoliv "normálních" a navíc na to je kvantum knihoven a dá se v tom suprově programovat.
Jinak když se podívám na svoje projektíky, tak chvíli to trvalo, ale v současné době už mám za sebou (a v procesu) pár projektů, na které jsem použil zčásti nebo úplně haskell. Jako v Java shopu asi není šance prorazit s čímkoliv jinám než Javou, na druhou stranu.... třeba dělal jsem webový "dotazník". Myslel jsem si, že to nějak splichtím v čemkoliv, jenže ve výsledku to bylo cca. 400 otázek s různými typy závislostí (když tohle je Ano, tohle nevyplňovat a tohle nastavit natvrdo na Ne apod.). Takže představa, že to budu dělat "normálně" vzala za své.
Takže jsem si napsal parser nějakých jednoduchých výrazů (většinou bool), nějaký jednoduchý popis toho dotazníku v Yamlu, frontend jsem měl v angularu, takže generátor javascriptu, který dělal online validaci a reakce všech těch položek na kliknutí (disable atp.), a generátor SQL create table se všemi nadefinovanými constrainty (to SQL má asi 450 řádek na 1 tabulku). Navíc ještě kontroloval, jestli jsou ty výrazy správně a nejsou tam překlepy v proměnných apod. Výsledek - po odladění to fungovalo naprosto bez chyby. Za ten rok, co to běželo, jsem nedostal jediný bug report - a to to v podstatě nikdo pořádně před nasazením netestoval.
A tohle je něco, s čím se při "normálním" programování můžeš setkat a v Haskellu se to řeší v zásadě dost triviálně. Připadá mi, že haskell má hodně dobrou podporu webových služeb, slušnou zásobu knihovan na normální práci. Takže řekl bych, že praktické využití je fakt velké, jen se člověk musí odhodlat. Ale pak zjistíš, že to odhodlání stojí za to:)
-
Takže jsem si napsal parser nějakých jednoduchých výrazů (většinou bool), nějaký jednoduchý popis toho dotazníku v Yamlu, frontend jsem měl v angularu, takže generátor javascriptu, který dělal online validaci a reakce všech těch položek na kliknutí (disable atp.), a generátor SQL create table se všemi nadefinovanými constrainty (to SQL má asi 450 řádek na 1 tabulku). Navíc ještě kontroloval, jestli jsou ty výrazy správně a nejsou tam překlepy v proměnných apod. Výsledek - po odladění to fungovalo naprosto bez chyby. Za ten rok, co to běželo, jsem nedostal jediný bug report - a to to v podstatě nikdo pořádně před nasazením netestoval.
A kdybys to rovnou napsal v Elmu, tak seš na tom stejně a ještě ti odpadne to generování javascriptu ;)
-
Já ti teď vůbec nerozumím. Řetězení lambd v monadu chápu jako řetězení ST () akcí. To se neděje. Vykonává se to stejně jako IO monad, dokonce vevnitř to je snad nějaký hodně podobný (akorát místo RealWorld tam je něco jiného). Co se může stát je, že STRef může obsahovat thunk (asi... nikdy jsem s tím nedělal), takže pokud to neforcneš, tak si v tom můžeš ten lazy pure výpočet nařetězit. Ale to nemá s ST nic společného. Ten 'zápis (toho thunku) do ST' se nikde neřetězí, ten se provede.
A proč teda podle tebe v tom příkladu dojde ke stack overflow?
No, vyhodnocuje se následující:
Obsah STRef: 0
po 1 kroku replicate:
Obsah STRef: (+ 1) 0
po 2 kroku replicate
Obsah STRef: (+ 1) . (+ 1) $ 0
atd.
Ty "kroky" se neřetězí. To je úplně stejný problém jako foldl (+) 0 [1..1000000]. Je to problém laziness v pure výpočtech, nikoliv řetězení v ST monadu.
-
Takže jsem si napsal parser nějakých jednoduchých výrazů (většinou bool), nějaký jednoduchý popis toho dotazníku v Yamlu, frontend jsem měl v angularu, takže generátor javascriptu, který dělal online validaci a reakce všech těch položek na kliknutí (disable atp.), a generátor SQL create table se všemi nadefinovanými constrainty (to SQL má asi 450 řádek na 1 tabulku). Navíc ještě kontroloval, jestli jsou ty výrazy správně a nejsou tam překlepy v proměnných apod. Výsledek - po odladění to fungovalo naprosto bez chyby. Za ten rok, co to běželo, jsem nedostal jediný bug report - a to to v podstatě nikdo pořádně před nasazením netestoval.
A kdybys to rovnou napsal v Elmu, tak seš na tom stejně a ještě ti odpadne to generování javascriptu ;)
Potřeboval jsem to napsat cca. před rokem a půl, angular jsem uměl, o Elmu jsem nevěděl a měl jsem na to míň než měsíc :) Dneska bych možná šáhnul po Elmu nebo Purescriptu, kdyby byl čas se to naučit (teď se učím purescript...)
-
No, vyhodnocuje se následující:
Obsah STRef: 0
po 1 kroku replicate:
Obsah STRef: (+ 1) 0
po 2 kroku replicate
Obsah STRef: (+ 1) . (+ 1) $ 0
atd.
Ty "kroky" se neřetězí. To je úplně stejný problém jako foldl (+) 0 [1..1000000]. Je to problém laziness v pure výpočtech, nikoliv řetězení v ST monadu.
Tak to jsme si asi v něčem nerozuměli, protože výrazu "(+ 1) . (+ 1) $ 0" já říkám zřetězení :)
Prostě bavili jsme se o tom, že v rámci STM se neděje* žádná tajemná nečistá inplace magie.
* alespoň formálně - v implementaci můžou být kdovíjaké figly
-
Tak to jsme si asi v něčem nerozuměli, protože výrazu "(+ 1) . (+ 1) $ 0" já říkám zřetězení :)
Prostě bavili jsme se o tom, že v rámci STM se neděje* žádná tajemná nečistá inplace magie.
* alespoň formálně - v implementaci můžou být kdovíjaké figly
Ale to není monadické řetězení. Neřetězí se ti ST akce, řetězí se ti aplikace funkce, ale ta nemá s ST vůbec nic společného. Řetězením v monadu rozumím například to, že když asociuješ tu >>= operaci opačně, tak to pak má třeba kvadratickou složitost apod. Což se myslím projevuje nějak u Pipe, kdy se to za určitých okolností takhle blbě může chovat. Ale tohle je trošku nad moje schopnosti (prý se to řeší nějakým "CoYoneda monadem" nebo co...)
-
Prostě bavili jsme se o tom, že v rámci STM se neděje* žádná tajemná nečistá inplace magie.
No ten STRef je v implementaci fakt normální mutable proměnná, akorát že ukazuje na thunk, čili může obsahovat výpočet. Jestli je schopne to při dostatečné striktnosti kompiler nějak vyoptimalizovat pryč, to netuším.
-
Ale to není monadické řetězení. Neřetězí se ti ST akce, řetězí se ti aplikace funkce, ale ta nemá s ST vůbec nic společného. Řetězením v monadu rozumím například to, že když asociuješ tu >>= operaci opačně, tak to pak má třeba kvadratickou složitost apod.
Ok, nechme to být, to by asi bylo na dlouho si vyjasnit, co kdo čím myslel :)
-
Prostě bavili jsme se o tom, že v rámci STM se neděje* žádná tajemná nečistá inplace magie.
No ten STRef je v implementaci fakt normální mutable proměnná, akorát že ukazuje na thunk, čili může obsahovat výpočet. Jestli je schopne to při dostatečné striktnosti kompiler nějak vyoptimalizovat pryč, to netuším.
Pokud bylo v proměnné x, tak (modifySTRef ref f) vytváří akci, která do ní zapisuje f(x). Proto když se to udělá mockrát za sebou, tak mi tam ten stack narůstá. Viz zdroják:
modifySTRef ref f = writeSTRef ref . f =<< readSTRef ref
Oproti tomu čárkovaná verze udělá to, že vytvoří akci, která zapíše přímo hodnotu f(x):
modifySTRef' ref f = do
x <- readSTRef ref
let x' = f x
x' `seq` writeSTRef ref x'
Že se tam neděje žádná nečistá magie myslím v tom smyslu, že je to stejně čisté jako IO - pořád se prostě operuje jenom se statickými akcemi, které jdou spustit jenom mimo jazyk samotný, stejně jako IO.
-
Oproti tomu čárkovaná verze udělá to, že vytvoří akci, která zapíše přímo hodnotu f(x):
Čili pokud to správně chápu, tak pokud bych měl fci f, která vyhodí výjimku, tak pokud udělám (modifySTRef ref f) a následně writeSTRef a readSTRef, tak se výjimka nevyhodí, zatímco kdybych použil modifySTRef', tak se vyhodí.
-
Oproti tomu čárkovaná verze udělá to, že vytvoří akci, která zapíše přímo hodnotu f(x):
Čili pokud to správně chápu, tak pokud bych měl fci f, která vyhodí výjimku, tak pokud udělám (modifySTRef ref f) a následně writeSTRef a readSTRef, tak se výjimka nevyhodí, zatímco kdybych použil modifySTRef', tak se vyhodí.
Ono ST je interně identické s IO a STRef je identický s IORef. Do obojího se ukládá Thunk, tzn. může být i nevykonaný.
A ano, jeden pohled, jak na to koukat je to, že v podstatě něco "interpretuje" ten ST/IO monad. A IMO je to rozumný pohled, pokud člověk zrovna neladí nějaké chyby v GHC :) GHC se samozřejmě snaží to kompilovat napřímo.
-
Čili pokud to správně chápu, tak pokud bych měl fci f, která vyhodí výjimku, tak pokud udělám (modifySTRef ref f) a následně writeSTRef a readSTRef, tak se výjimka nevyhodí, zatímco kdybych použil modifySTRef', tak se vyhodí.
Tak vyzkoušeno, potvrzeno:
import Data.STRef
import Control.Monad.ST
err _ = error "Ha!"
main = do
print $ runST $ do
ref <- newSTRef 0
modifySTRef ref err
writeSTRef ref 1
readSTRef ref
- při použití modifySTRef vypíše "1", při použití modifySTRef' vypíše "Ha!".
-
POZOR REKLAMA POZOR REKLAMA POZOR REKLAMA :)
Kdybyste někdo měli zájem, tento čtvrtek 19.5. se v Brně koná první sraz zájemců o Elm:
http://www.meetup.com/Brno-Elm-Meetup/events/230668222/
-
Tak ten vtip zní "A monad is just a monoid in the category of endofunctors". Tak prozatím to chápu tak, že "monoid" znamená v podstatě "list" nebo "stream" a "kategorie endofunktorů" znamená, že jsem z té akce v listu schopen vytáhnout hodnotu a nějak ji předat následujícím akcím..
Tak to chápeš blbě :) Monoid znamená, že pro tu věc platí pravidla monoidu (asociativita a existence neutrálního prvku) a endofunktor je funktor, který mapuje kategorii na sebe samu.
Endofunktor je to proto, že libovolný haskellovský typ mapuje opět na haskellovský typ. A monoid je to proto, že bind je asociativní a return je neutrální prvek.
...ale neřekl bych, že by komukoli "normálnímu" tahle definice pomohla pochopit, o co jde :)
On to hlavně není vtip, takto to formuloval Mac Lane (vytrženo z kontextu to zní ovšem trochu abstrakně).
Pochopit tu definici je užitečné k hlubšímu vhledu do problematiky, ale nehodí se k prvotnímu vysvětlení.
-
Tak ten vtip zní "A monad is just a monoid in the category of endofunctors". Tak prozatím to chápu tak, že "monoid" znamená v podstatě "list" nebo "stream" a "kategorie endofunktorů" znamená, že jsem z té akce v listu schopen vytáhnout hodnotu a nějak ji předat následujícím akcím..
Tak to chápeš blbě :) Monoid znamená, že pro tu věc platí pravidla monoidu (asociativita a existence neutrálního prvku) a endofunktor je funktor, který mapuje kategorii na sebe samu.
Endofunktor je to proto, že libovolný haskellovský typ mapuje opět na haskellovský typ. A monoid je to proto, že bind je asociativní a return je neutrální prvek.
...ale neřekl bych, že by komukoli "normálnímu" tahle definice pomohla pochopit, o co jde :)
On to hlavně není vtip, takto to formuloval Mac Lane (vytrženo z kontextu to zní ovšem trochu abstrakně).
Pochopit tu definici je užitečné k hlubšímu vhledu do problematiky, ale nehodí se k prvotnímu vysvětlení.
... a nebyl by ochoten mi to někdo teda vysvětlit? Když už jsme u toho, tak by mě to docela zajímalo. Monoid je asociativní - monoid je operace >>=. Operace >>= je řetězení operací za sebe. V monadu to znamená řetězení instrukcí za sebe (<> taky může počítat maximum). Takže se na to taky dá dívat jako na List, který interpret pomocí fold (>>=) interpretuje. Nebo nedá?
A endofunktor v té definici teda znamená co?
-
A endofunktor v té definici teda znamená co?
Ten endofunktor je "funkce" z typu na typ. Ta dostane na vstup nějaký typ T a vrátí nový typ "seznam T" nebo "akce vracející T" a podobně. Nehodí se to k prvotnímu vysvětlení, protože málokdo uvažuje stylem že typ "pole intů" získá tak že vezme typ "int" a prožene ho nějakou "funkcí" na úrovni typového systému.
-
Ještě bych měl asi dodat, že tohle se daleko líp chápe na něčem jednodušším než je Monád. Koukni se na Funktor. Tam je ta představa, že nějakou "funkcí" udělám z nějakého typu jejich seznam a z jednoduchých funkcí funkce pro seznamy, trochu jasnější.
-
... a nebyl by ochoten mi to někdo teda vysvětlit? Když už jsme u toho, tak by mě to docela zajímalo. Monoid je asociativní - monoid je operace >>=. Operace >>= je řetězení operací za sebe. V monadu to znamená řetězení instrukcí za sebe (<> taky může počítat maximum). Takže se na to taky dá dívat jako na List, který interpret pomocí fold (>>=) interpretuje. Nebo nedá?
A endofunktor v té definici teda znamená co?
Docela dobrý vysvětlení máš tady: http://stackoverflow.com/questions/3870088/a-monad-is-just-a-monoid-in-the-category-of-endofunctors-whats-the-issue ale podle mě nemá smysl si s tím lámat hlavu. Pokud tě aspoň úvod do teorie kategorií zajímá, tak bych vřele doporučil http://www1.eafit.edu.co/asr/pubs/others/cain-screen.pdf - je to nejsrozumitelnější popis pro programátora, jakej jsem zatím viděl. Už kolem strany 30 by ti to mělo začít být jasný a v kapitole 6.2.1 je to pak úplně explcitně.
-
Tak ten vtip zní "A monad is just a monoid in the category of endofunctors". Tak prozatím to chápu tak, že "monoid" znamená v podstatě "list" nebo "stream" a "kategorie endofunktorů" znamená, že jsem z té akce v listu schopen vytáhnout hodnotu a nějak ji předat následujícím akcím..
Tak to chápeš blbě :) Monoid znamená, že pro tu věc platí pravidla monoidu (asociativita a existence neutrálního prvku) a endofunktor je funktor, který mapuje kategorii na sebe samu.
Endofunktor je to proto, že libovolný haskellovský typ mapuje opět na haskellovský typ. A monoid je to proto, že bind je asociativní a return je neutrální prvek.
...ale neřekl bych, že by komukoli "normálnímu" tahle definice pomohla pochopit, o co jde :)
On to hlavně není vtip, takto to formuloval Mac Lane (vytrženo z kontextu to zní ovšem trochu abstrakně).
Pochopit tu definici je užitečné k hlubšímu vhledu do problematiky, ale nehodí se k prvotnímu vysvětlení.
... a nebyl by ochoten mi to někdo teda vysvětlit? Když už jsme u toho, tak by mě to docela zajímalo. Monoid je asociativní - monoid je operace >>=. Operace >>= je řetězení operací za sebe. V monadu to znamená řetězení instrukcí za sebe (<> taky může počítat maximum). Takže se na to taky dá dívat jako na List, který interpret pomocí fold (>>=) interpretuje. Nebo nedá?
A endofunktor v té definici teda znamená co?
Předpokládám znalost definice monoidu a základních pojmů z teorie kategorií. Místo nosné množiny se vezme nějaký endofunktor nad nějakou kategorií, řekněme T. Místo operace nad monoidem (něříkám násobení, taky může být aditivní, prostě nějaká funkce o dvou parametrech tak, aby to byl monoid) se vezme přirozená transformace μ (jako multiplication) T∘T do T a místo unit elementu se vezme přirozená transformace η (jako identity) 1 do T (1 je id endofunktor). Nejdou tu kreslit komutativní diagramy, takže v symbolech: μ∘Tμ=μ∘μT a μ∘Tη=μ∘ηT=1 (první rovnost si vynucuje asociativitu a druhá identitu zleva a zprava). Žádná magie v tom není.
-
POZOR REKLAMA POZOR REKLAMA POZOR REKLAMA :)
Kdybyste někdo měli zájem, tento čtvrtek 19.5. se v Brně koná první sraz zájemců o Elm:
http://www.meetup.com/Brno-Elm-Meetup/events/230668222/
Bude k tomu nějaký materiál?
-
Bude k tomu nějaký materiál?
Myslíš třeba jako kilo koksu? :)
Je to první setkání, asi spíš jenom pokec.
-
Bude k tomu nějaký materiál?
Myslíš třeba jako kilo koksu? :)
...
;D ;D ;D
Tak to zřejmě bude parádní a výživnej pokec!
-
Bude k tomu nějaký materiál?
Myslíš třeba jako kilo koksu? :)
Je to první setkání, asi spíš jenom pokec.
To neberu/koksem netopím (neznám tě tak dobře, abych tvou otázku mohl úspěšně disambiguovat ;)).
-
Bude k tomu nějaký materiál?
Myslíš třeba jako kilo koksu? :)
Je to první setkání, asi spíš jenom pokec.
To neberu/koksem netopím (neznám tě tak dobře, abych tvou otázku mohl úspěšně disambiguovat ;)).
Kilem koksu by se ani nezahřál kotel, ale na tohle by to bylo asi akorát: https://www.youtube.com/watch?v=tUxOdFJEDdg (https://www.youtube.com/watch?v=tUxOdFJEDdg)
-
Mimochode, jak se v Haskell řeší mutable stav? Je zde například key-value úložiště?
-
Mimochode, jak se v Haskell řeší mutable stav? Je zde například key-value úložiště?
mutable stave bez IO:
https://hackage.haskell.org/package/base-4.8.2.0/docs/Data-STRef.html
úložiště (jedno z mnoha):
https://hackage.haskell.org/package/acid-state
-
Mimochode, jak se v Haskell řeší mutable stav? Je zde například key-value úložiště?
Mutable stav se typicky řeší přes nějaký State monad, abys nepotřeboval mutable stav. Takže pak máš třeba "HashTable" (spíš HashMap), která je sice immutable, ale když ji potřebuješ měnit, tak prostě poběžíš v state/stateT a pak můžeš psát třeba:
flip runStateT mempty $ do
at "key" .= 10
over (ix "key") (+ 1)
over (ix "does-not-exist") (+ 20)
newvalue <- use (at "key")
whenJustM (use (at "key")) print
Ad key-value úložiště - jestli myslíš diskové, tak acid-state, ale moje osobní zkušenost je, že je lepší použít persistent nad PostgreSQL. Jinak pro haskell existují knihovny pro hodně věcí jako Redis apod.
Jinak docela hezký je třeba vault (https://hackage.haskell.org/package/vault), který má jako klíč "typ".
-
Mimochode, jak se v Haskell řeší mutable stav? Je zde například key-value úložiště?
O tom je celé tohle vlákno: monády, monády, monády...
-
Mimochode, jak se v Haskell řeší mutable stav? Je zde například key-value úložiště?
O tom je celé tohle vlákno: monády, monády, monády...
Však jsem za tohle vlákno v této podobě docela vděčný. Bylo tady řečeno víc, než na co jsem se ptal a díky němu mám i s čím experimentovat.
-
Však jsem za tohle vlákno v této podobě docela vděčný. Bylo tady řečeno víc, než na co jsem se ptal a díky němu mám i s čím experimentovat.
Myslím, že bys klidně mohl výsledky experimentů i postovat. Je tady několik praktických haskellistů, kteří určitě rádi nasměují/poradí/prodiskutují - a my, nehaskellisti, si to se zájmem poslechneme, jak se praktické věci v haskellu řeší.
-
Však jsem za tohle vlákno v této podobě docela vděčný. Bylo tady řečeno víc, než na co jsem se ptal a díky němu mám i s čím experimentovat.
Myslím, že bys klidně mohl výsledky experimentů i postovat. Je tady několik praktických haskellistů, kteří určitě rádi nasměují/poradí/prodiskutují - a my, nehaskellisti, si to se zájmem poslechneme, jak se praktické věci v haskellu řeší.
Ono toho zatím moc není. Mám ještě stále problémy se čtením symbolů, které bývají v jiných jazycích slovní, ale v Haskellu jsou tvořeny posloupností nealfanumerických znaků. Časem si na to zvyknu a teprve pak to ocením.
-
Ono toho zatím moc není. Mám ještě stále problémy se čtením symbolů, které bývají v jiných jazycích slovní, ale v Haskellu jsou tvořeny posloupností nealfanumerických znaků. Časem si na to zvyknu a teprve pak to ocením.
Mě pomohlo, když jsem si uvědomil, že veškerý kód v Haskellu se skládá z pouhých dvou tokenů: mezery a názvu výrazu. A pak závorky.
-
Ono toho zatím moc není. Mám ještě stále problémy se čtením symbolů, které bývají v jiných jazycích slovní, ale v Haskellu jsou tvořeny posloupností nealfanumerických znaků. Časem si na to zvyknu a teprve pak to ocením.
Tohle mě třeba taky štve. Číst kód, který je směsicí >> >>= << <=< $ <$> kde některé z nich fungují "zprava doleva" a některé naopak, je peklo. Občas mám pocit, že to snad haskellisti dělají schválně, aby okolí ukázali "já na to mám" ;)
-
Ono toho zatím moc není. Mám ještě stále problémy se čtením symbolů, které bývají v jiných jazycích slovní, ale v Haskellu jsou tvořeny posloupností nealfanumerických znaků. Časem si na to zvyknu a teprve pak to ocením.
Mě pomohlo, když jsem si uvědomil, že veškerý kód v Haskellu se skládá z pouhých dvou tokenů: mezery a názvu výrazu. A pak závorky.
To platí pro Lisp, ale Haskell toho má podstatně víc. Symboly => -> :: apod. jsou pro mne stále stěží čitelné.
Kromě toho mezera přece není tokenem, ale pouze oddělovačem tokenů, ne? Jinak žádnou funkci nemá.
-
To platí pro Lisp, ale Haskell toho má podstatně víc. Symboly => -> :: apod. jsou pro mne stále stěží čitelné.
Myslím, že jste si teď s BoneFlute nerozuměli, každý mluvíte o něčem jiným.
Kromě toho mezera přece není tokenem, ale pouze oddělovačem tokenů, ne? Jinak žádnou funkci nemá.
Ještě jako indentation. Haskell má významný whitespace (narozdíl třeba od javy).
-
Ono toho zatím moc není. Mám ještě stále problémy se čtením symbolů, které bývají v jiných jazycích slovní, ale v Haskellu jsou tvořeny posloupností nealfanumerických znaků. Časem si na to zvyknu a teprve pak to ocením.
Mě pomohlo, když jsem si uvědomil, že veškerý kód v Haskellu se skládá z pouhých dvou tokenů: mezery a názvu výrazu. A pak závorky.
To platí pro Lisp, ale Haskell toho má podstatně víc. Symboly => -> :: apod. jsou pro mne stále stěží čitelné.
No, tak jsem si docela dost jistý, že třeba <- je funkce. A vůbec by měl nepřekvapilo, kdyby :: byla funkce pro definování typové anotace.
-
No, tak jsem si docela dost jistý, že třeba <- je funkce.
Standardni fce to neni. Je to syntakticky cukr pouzitelny jenom v do blocku. do taky fce neni.
-
Ono toho zatím moc není. Mám ještě stále problémy se čtením symbolů, které bývají v jiných jazycích slovní, ale v Haskellu jsou tvořeny posloupností nealfanumerických znaků. Časem si na to zvyknu a teprve pak to ocením.
Tohle mě třeba taky štve. Číst kód, který je směsicí >> >>= << <=< $ <$> kde některé z nich fungují "zprava doleva" a některé naopak, je peklo. Občas mám pocit, že to snad haskellisti dělají schválně, aby okolí ukázali "já na to mám" ;)
To je jen o zvyku, ne? Je to prostě jen konvence.
-
To je jen o zvyku, ne? Je to prostě jen konvence.
Tak jasne, zvyknout se da i na koncentrak...
-
... Symboly => -> :: apod. jsou pro mne stále stěží čitelné.
No, tak jsem si docela dost jistý, že třeba <- je funkce. A vůbec by měl nepřekvapilo, kdyby :: byla funkce pro definování typové anotace.
Ve škole jsme takové symboly vůbec nepoužívali, takže nemám na co navazovat. Proto jsou pro mne nové.
-
... Symboly => -> :: apod. jsou pro mne stále stěží čitelné.
No, tak jsem si docela dost jistý, že třeba <- je funkce. A vůbec by měl nepřekvapilo, kdyby :: byla funkce pro definování typové anotace.
Ve škole jsme takové symboly vůbec nepoužívali, takže nemám na co navazovat. Proto jsou pro mne nové.
No tak to je jasný.
Jsem spíše narážel na to, že z tradičnějších jazyků je člověk zvyklí na takovou tu omáčku: class, function, return, ... A v Haskellu není prostě nic. Jen výrazy. Každý kousíček kódu je nějaká funkce, která má nějakou signaturu, což znamená, že má někde vstup a vrací jeden výstup. (A ok, pár výjimek by se tam samozřejmě našlo.)
Třeba == je funkce. Nebo [] je konstruktor, taky funkce. (Teď doufám, že už se neseknu.)
A oproti Lispu má výhodu v té funkcionálnosti. Že se dá na funkce koukat jako na krabičky, které mají jasně odlišený vnitřek a venek. Což zase ohromě osvobozuje myšlení. Ale tady už skutečně odjíždím do offtopicu.
-
... Symboly => -> :: apod. jsou pro mne stále stěží čitelné.
No, tak jsem si docela dost jistý, že třeba <- je funkce. A vůbec by měl nepřekvapilo, kdyby :: byla funkce pro definování typové anotace.
Ve škole jsme takové symboly vůbec nepoužívali, takže nemám na co navazovat. Proto jsou pro mne nové.
No tak to je jasný.
Jsem spíše narážel na to, že z tradičnějších jazyků je člověk zvyklí na takovou tu omáčku: class, function, return, ... A v Haskellu není prostě nic. Jen výrazy. Každý kousíček kódu je nějaká funkce, která má nějakou signaturu, což znamená, že má někde vstup a vrací jeden výstup. (A ok, pár výjimek by se tam samozřejmě našlo.)
Třeba == je funkce. Nebo [] je konstruktor, taky funkce. (Teď doufám, že už se neseknu.)
A oproti Lispu má výhodu v té funkcionálnosti. Že se dá na funkce koukat jako na krabičky, které mají jasně odlišený vnitřek a venek. Což zase ohromě osvobozuje myšlení. Ale tady už skutečně odjíždím do offtopicu.
Absence "omáčky" je záměrná, protože to je výhoda, mozek ji nemusí kognitivně odfiltrovávat.
-
Absence "omáčky" je záměrná, protože to je výhoda, mozek ji nemusí kognitivně odfiltrovávat.
Přesně tak.
-
Jsem spíše narážel na to, že z tradičnějších jazyků je člověk zvyklí na takovou tu omáčku: class, function, return, ... A v Haskellu není prostě nic. Jen výrazy. Každý kousíček kódu je nějaká funkce, která má nějakou signaturu, což znamená, že má někde vstup a vrací jeden výstup. (A ok, pár výjimek by se tam samozřejmě našlo.)
Třeba == je funkce. Nebo [] je konstruktor, taky funkce. (Teď doufám, že už se neseknu.)
A oproti Lispu má výhodu v té funkcionálnosti. Že se dá na funkce koukat jako na krabičky, které mají jasně odlišený vnitřek a venek. Což zase ohromě osvobozuje myšlení. Ale tady už skutečně odjíždím do offtopicu.
Absence "omáčky" je záměrná, protože to je výhoda, mozek ji nemusí kognitivně odfiltrovávat.
To je mi také jasné. Když se podíváš třeba na Klingonštinu, tak tam také nemusíš nic odfiltrovávat. Vše je jasné na první pohled.
Jsou jazyky, kde je té omáčky hromada. Nejznámější bude asi Cobol, pak SQL, Visual Basic, ... Tam je té omáčky až příliš a zhoršuje čitelnost. Programy/skripty bývají tak rozvláčné, že se často musí zalamovat řádky i uvnitř příkazu.
Lisp šel opačnou cestou - omáčka prakticky žádná. Nadává se mu, že je "samá blbá závorka". Přitom je těch závorek mnohem méně, než ve srovnatelné aplikaci např. v Javě. Redukcí omáčky se zhoršila čitelnost.
Haskell šel cestou, kde omáčková slova nahradil symboly. Pro začátečníka ta hromada nepochopitelných značek je velkou bariérou. Pokročilý uživatel si libuje, že se mu to krásně a rychle čte. A mám takový pocit, že se začátečníkům (ne)skrytě pochechtává.
-
Haskell šel cestou, kde omáčková slova nahradil symboly. Pro začátečníka ta hromada nepochopitelných značek je velkou bariérou.
Jen upřesním. Ono nejde tak docela o náhradu. V Haskelu se ta omáčková slova ničím nenahradila. Ony se prostě vypustily.
Jestli je to velkou bariérou... no jsem celkem průměrný vývojář, ale mě to šlo docela rychle.
-
Haskell šel cestou, kde omáčková slova nahradil symboly. Pro začátečníka ta hromada nepochopitelných značek je velkou bariérou.
Jen upřesním. Ono nejde tak docela o náhradu. V Haskelu se ta omáčková slova ničím nenahradila. Ony se prostě vypustily.
Jestli je to velkou bariérou... no jsem celkem průměrný vývojář, ale mě to šlo docela rychle.
A co ty :: >>= <- -> apod? Tyto symboly se v jiných jazycích nepoužívají nebo např. -> v má v PHP jiný význam. Je to prostě nezvyk, proto jsem to označil za bariéru.
-
Haskell šel cestou, kde omáčková slova nahradil symboly. Pro začátečníka ta hromada nepochopitelných značek je velkou bariérou.
Jen upřesním. Ono nejde tak docela o náhradu. V Haskelu se ta omáčková slova ničím nenahradila. Ony se prostě vypustily.
Jestli je to velkou bariérou... no jsem celkem průměrný vývojář, ale mě to šlo docela rychle.
A co ty :: >>= <- -> apod? Tyto symboly se v jiných jazycích nepoužívají nebo např. -> v má v PHP jiný význam.
:: - to je trivka
-> - argumenty funkce, šipka místo čárky a poslední je return, trivka
>>=, <- - to jsou monády, jo, to je horší no
Pomohlo mi, když jsem si definici:
a b = b + 1
přeložil tak, že to rovná se znamená, že to znamená, že je to to samý.
A pak už to šlo jeden aha efekt za druhým :-)
Je to prostě nezvyk, proto jsem to označil za bariéru.
Však já ti neberu.
Tyto symboly se v jiných jazycích nepoužívají nebo např. -> v má v PHP jiný význam.
Což teprve takové C a jejich pointerová aritemetika...
-
:: - to je trivka
Jak pro koho. Pro mne to byla novinka.
-> - argumenty funkce, šipka místo čárky a poslední je return, trivka
Šipka místo čárky. Koho by to napadlo, že? Naštěstí se mi to nedávno podařilo rozkódovat.
Kromě toho se šipka používá v kontextu do pro výstup. Na to jsi jaksi zapomněl.
>>=, <- - to jsou monády, jo, to je horší no
Hmm, a zase nic. Kdybych to nevěděl z předchozí diskuze, tak by to bylo pro mne +0.
Pomohlo mi, když jsem si definici:
a b = b + 1
přeložil tak, že to rovná se znamená, že to znamená, že je to to samý.
Tohle byla jedna z prvních věcí, kterou jsem pochopil. Zapomněl jsi však uvést, že "a" je jméno funkce. Kromě toho se "a" používá i jako zástupný symbol typu v definici parametrů. Přesto jsi ho použil jako jméno funkce, abys mi to co nejvíc zamlžil. To je tak těžké napsat tohle?
f a = a + 1
A pak už to šlo jeden aha efekt za druhým :-)
No, já nevím. FP jsme se nikdy neučili ani náznakem. OOP jen velmi okrajově - musel jsem se to naučit sám, což bylo vlastně dobře.
Když to shrnu, tak v tom svém příspěvku jsi mi nic nevysvětlil - akorát že je to trivka. Není to výtka na tebe, protože mám pocit, že to dělají všichni haskellisti. Jakmile to pochopí, ztratí schopnost to vysvětlit.
Tyto symboly se v jiných jazycích nepoužívají nebo např. -> v má v PHP jiný význam.
Což teprve takové C a jejich pointerová aritemetika...
V C++ naštěstí nedělám. To je jazyk jak pro masochisty.
-
Když to shrnu, tak v tom svém příspěvku jsi mi nic nevysvětlil - akorát že je to trivka. Není to výtka na tebe, protože mám pocit, že to dělají všichni haskellisti. Jakmile to pochopí, ztratí schopnost to vysvětlit.
Aha, jenže problém je v tom, že já jsem se nesnažil ti nic vysvětlovat. Já jsem jen reagoval na to, že je to změť znaků.
Pokud by si řekl, že chceš pomoct to naučit, tak to tě rovnou odkážu na http://naucte-se.haskell.cz (http://naucte-se.haskell.cz). Tam jsem začínal, a je to super.
-
-> - argumenty funkce, šipka místo čárky a poslední je return, trivka
Šipka místo čárky. Koho by to napadlo, že? Naštěstí se mi to nedávno podařilo rozkódovat.
Kromě toho se šipka používá v kontextu do pro výstup. Na to jsi jaksi zapomněl.
Nezapoměl. Nepoužívá.
-
Když to shrnu, tak v tom svém příspěvku jsi mi nic nevysvětlil - akorát že je to trivka. Není to výtka na tebe, protože mám pocit, že to dělají všichni haskellisti. Jakmile to pochopí, ztratí schopnost to vysvětlit.
Aha, jenže problém je v tom, že já jsem se nesnažil ti nic vysvětlovat. Já jsem jen reagoval na to, že je to změť znaků.
Pokud by si řekl, že chceš pomoct to naučit, tak to tě rovnou odkážu na http://naucte-se.haskell.cz (http://naucte-se.haskell.cz). Tam jsem začínal, a je to super.
Myslel jsem si, že mi chceš vysvětlit, proč to není změť znaků. Nevysvětlil jsi nic. Pouze jsi reagoval.
Odkud myslíš, že se Haskell učím? Jedna věc je naučit se jazyk, další je pochopit, procvičit a naučit se to skutečně používat. Ty jen napíšeš, že je to trivka a že je to super. Taková rada je mi k ho...
-
Když to shrnu, tak v tom svém příspěvku jsi mi nic nevysvětlil - akorát že je to trivka. Není to výtka na tebe, protože mám pocit, že to dělají všichni haskellisti. Jakmile to pochopí, ztratí schopnost to vysvětlit.
Aha, jenže problém je v tom, že já jsem se nesnažil ti nic vysvětlovat. Já jsem jen reagoval na to, že je to změť znaků.
Pokud by si řekl, že chceš pomoct to naučit, tak to tě rovnou odkážu na http://naucte-se.haskell.cz (http://naucte-se.haskell.cz). Tam jsem začínal, a je to super.
Myslel jsem si, že mi chceš vysvětlit, proč to není změť znaků. Nevysvětlil jsi nic. Pouze jsi reagoval.
Odkud myslíš, že se Haskell učím? Jedna věc je naučit se jazyk, další je pochopit, procvičit a naučit se to skutečně používat. Ty jen napíšeš, že je to trivka a že je to super. Taková rada je mi k ho...
OK. Tak přeji hodně štěstí.
-
Tohle byla jedna z prvních věcí, kterou jsem pochopil. Zapomněl jsi však uvést, že "a" je jméno funkce. Kromě toho se "a" používá i jako zástupný symbol typu v definici parametrů. Přesto jsi ho použil jako jméno funkce, abys mi to co nejvíc zamlžil. To je tak těžké napsat tohle?
f a = a + 1
V Haskellu je nekolik konceptu, ktery clovek bezpodminecne musi spravne pochopit, jinak se v dalsim utopi (v nejhorsim pripade si bude myslet, ze to chape, ale pochopi to blbe):
1. promenne nejsou promenne ve smyslu imperativnich jazyku ("misto, kam dam nejakou hodnotu"), ale "nalepky pro hodnoty" - viz http://stackoverflow.com/questions/29967086/are-elixir-variables-really-immutable (tady se mluvi o Elixiru, ale v Haskellu je to totez). Je potreba rozlisovat volnou a vazanou promennou (tohle je spis prologovska hantyrka, ale funguje to stejne).
2. v Haskellu plati referencni transparentnost = jestlize plati a=b, tak kdekoli mam a, tam muzu dat b, a program bude fungovat porad stejne.
3. V Haskellu funguje currifikace "automaticky" - jestlize mam fci f dvou promennych, tak (f 1) je funkce jedne promenne. V jinych jazycich bych to musel udelat explicitne lambdou: lambda x: f(1,x).
Tohle je taky ten duvod, proc se v typovych anotacich pouzivaji ty divne sipky a ne carky. Kdyz mam treba
f :: Int -> String -> String
a f aplikuju na integer, dostanu:
g :: String -> String
...takze je to vlastne
f :: Int -> (String -> String)
- a ty zavorky je tam zbytecne psat, proto se tam nepisou.
Zaroven je tohle vec, ktera taky zpusobuje horsi citelnost pro lidi zvykly na jiny jazyky, napr:
filter :: (a -> Bool) -> [a] -> [a]
myFilter = filter (\x -> True)
by clovek nenavykly na "automatickou" currifikaci napsal spis takhle:
myFilter list = filter (\x -> True) list
ale haskellisti to nedelaji. Zhorsuje to citelnost, protoze si clovek musi v hlave odvozovat parametry leve strany z prave.
4. pravidla zapisu operatoru - "symboly" jsou defaultne infixove, "slova" prefixova. Symboly se prefixove zapisuji pomoci zavorek, slova infixove pomoci zpetnych apostrofu:
1 + 1 ~ (+) 1 1
filter (\x -> True) [1] ~ (\x -> True) `filter` [1]
5. Type konstruktor neni data konstruktor. Data konstruktor je normalni funkce. Type konstruktor je "funkce o level vys". Bohuzel (pokud se nepletu) Haskell umoznuje pouzit stejne jmeno pro oboji (neni to konflikt):
data Maybe a = Nothing | Just a
Prelude> :t Just
Just :: a -> Maybe a
Prelude> :k Maybe
Maybe :: * -> *
Prelude> :k Just
<interactive>:1:1:
Not in scope: type constructor or class ‘Just’
A data constructor of that name is in scope; did you mean DataKinds?
Prelude> :t Maybe
<interactive>:1:1:
Not in scope: data constructor ‘Maybe’
Perhaps you meant variable ‘maybe’ (imported from Prelude)
6. Definice data konstruktoru i type konstruktoru muzou pouzivat promenne obdobne jako definice jinych fci:
Prelude> :k Maybe
Maybe :: * -> *
Prelude> :k Maybe String
Maybe String :: *
7. I kdyz je do block navrzeny tak, aby vypadal, ze v nem vyse zminene neplati, tak je to jenom syntakticky cukr, ve kterem porad plati vsechno.
---
Kdyz si tyhle veci zazijes, das uz v Haskellu celkem cokoli, s vetsim nebo mensim usilim ;)
-
Bohuzel (pokud se nepletu) Haskell umoznuje pouzit stejne jmeno pro oboji (neni to konflikt):
Tak nepletu:
Prelude> data Test a = Test a
Prelude> :t Test
Test :: a -> Test a
Prelude> :k Test
Test :: * -> *
Tohle mi neprijde jako stastna volba. Minimalne zacatecniky to muze pekne mast.
-
1. promenne nejsou promenne ve smyslu imperativnich jazyku ...
2. v Haskellu plati referencni transparentnost ...
3. V Haskellu funguje currifikace "automaticky" ...
4. pravidla zapisu operatoru - "symboly" jsou defaultne infixove, "slova" prefixova....
5. Type konstruktor neni data konstruktor. Data konstruktor je normalni funkce...
6. Definice data konstruktoru i type konstruktoru muzou pouzivat promenne obdobne jako definice jinych fci...
7. I kdyz je do block navrzeny tak, aby vypadal, ze v nem vyse zminene neplati, tak je to jenom syntakticky cukr, ve kterem porad plati vsechno...
---
Kdyz si tyhle veci zazijes, das uz v Haskellu celkem cokoli, s vetsim nebo mensim usilim ;)
Body 1. a 2. jsem pochopil téměř ihned, protože v XSLT je to podobné. 3. - s currifikací se budu muset ještě trochu porvat. 4. bod jsem pochopil z webových stránek. 5. jsem zkoušel, ale teď teprve vím, že "data" je jen funkce a začíná mi to dávat logiku. 6. mi z toho vyplývá. 7. je mi jasná již z předchozích komentů v tomto vláknu.
Takže díky, zbývá mi jen zkoušet a pilovat různé kombinace toho, co jsem se (nejen) tady dozvěděl.
-
5. jsem zkoušel, ale teď teprve vím, že "data" je jen funkce a začíná mi to dávat logiku.
"data" samo o sobě je klíčové slovo, ne funkce. To, o co jde, je, že data contructor je vlastně funkce. Když mám třeba
Prelude> data MujParadniTyp = PismenkovaHodnota String | CiselnaHodnota Int
tak máme vlastně dvě funkce, které mi ze Stringu nebo Intu vytvoří MujParadniTyp
Prelude> :t PismenkovaHodnota
PismenkovaHodnota :: String -> MujParadniTyp
Prelude> :t CiselnaHodnota
CiselnaHodnota :: Int -> MujParadniTyp
- v podstatě tu hodnotu zavřou do krabice. Krabice může obsahovat String nebo Int hodnotu a krabice samotná má typ MujParadniTyp. V pythonu by se pro podobnej efekt použil tuple:
("PismenkovaHodnota","hola hola")
("CiselnaHodnota",1)
by odpovídalo Haskellovskému
(PismenkovaHodnota "hola hola")
(CiselnaHodnota 1)
- v Haskellu se krabice prostě dá udělat funkcí a můžu pak dělat pattern matching, to by ve většině jazyků nešlo - funkce je pro ně něco, co jde jenom spustit. Pro Haskell ne :)
A tohodle "zavírání do krabic" se právě chytře používá mj. v těch monádách. A protože krabice i její obsah je vždycky haskellovský typ, tak ta funkce CiselnaHodnota je ze stejné kategorie do stejné kategorie a je to ... tramtadadá! ... ten slavný endofunktor! :) (EDIT: teda respektive byl by, pokud by byl nadefinovaný pro libovolný typ, ne jenom pro String)
-
Pár praktických detailů, které mě potrápily:
Aplikace funkce má nejvyšší prioritu. Vždycky. Všude. Tzn.
a b c !!> x y z <* u e >> z p p
je
(a b c) !!> (x y z) <* (u e) >> (z p p)
A funktory se závorkují následovně:
f <$> a <*> b <*> c
je
((f <$> a) <*> b) <*> c
-
Aplikace funkce má nejvyšší prioritu. Vždycky. Všude. Tzn.
Ono je to bohužel složitější. Je to spíš tak, že operátor (bez závorek) nemůže být argumentem funkce.
list = map foo $ xs
není
list = (map foo $) xs
což by muselo být, pokud by platilo to, co píšeš.
(příklad z http://stackoverflow.com/questions/3125395/haskell-operator-vs-function-precedence)
Haskell sice příjemně snížil nutnost psaní závorek, ale za cenu mírného zmatení. Je to ale o zvyku. Elixir to má taky, jenom trochu jinak.
-
Aplikace funkce má nejvyšší prioritu. Vždycky. Všude. Tzn.
Ono je to bohužel složitější. Je to spíš tak, že operátor (bez závorek) nemůže být argumentem funkce.
Když jsem to psal, tak jsem přemýšlel, jak to napsat správně... pak jsem to vzdal a radši jsem napsal ten příklad :)
-
5. jsem zkoušel, ale teď teprve vím, že "data" je jen funkce a začíná mi to dávat logiku.
"data" samo o sobě je klíčové slovo, ne funkce. To, o co jde, je, že data contructor je vlastně funkce. Když mám třeba
Prelude> data MujParadniTyp = PismenkovaHodnota String | CiselnaHodnota Int
tak máme vlastně dvě funkce, které mi ze Stringu nebo Intu vytvoří MujParadniTyp
Prelude> :t PismenkovaHodnota
PismenkovaHodnota :: String -> MujParadniTyp
Prelude> :t CiselnaHodnota
CiselnaHodnota :: Int -> MujParadniTyp
- v podstatě tu hodnotu zavřou do krabice. Krabice může obsahovat String nebo Int hodnotu a krabice samotná má typ MujParadniTyp. V pythonu by se pro podobnej efekt použil tuple:
("PismenkovaHodnota","hola hola")
("CiselnaHodnota",1)
by odpovídalo Haskellovskému
(PismenkovaHodnota "hola hola")
(CiselnaHodnota 1)
- v Haskellu se krabice prostě dá udělat funkcí a můžu pak dělat pattern matching, to by ve většině jazyků nešlo - funkce je pro ně něco, co jde jenom spustit. Pro Haskell ne :)
A tohodle "zavírání do krabic" se právě chytře používá mj. v těch monádách. A protože krabice i její obsah je vždycky haskellovský typ, tak ta funkce CiselnaHodnota je ze stejné kategorie do stejné kategorie a je to ... tramtadadá! ... ten slavný endofunktor! :) (EDIT: teda respektive byl by, pokud by byl nadefinovaný pro libovolný typ, ne jenom pro String)
Teď jsi to napsal trochu zmateně a nejsem si jist, že úplně správně.
-
Teď jsi to napsal trochu zmateně a nejsem si jist, že úplně správně.
Proč zmateně a včem myslíš, že ne správně? (nehádám se, je to možný, proto mě to zajímá)
-
Teď jsi to napsal trochu zmateně a nejsem si jist, že úplně správně.
Proč zmateně a včem myslíš, že ne správně? (nehádám se, je to možný, proto mě to zajímá)
Matoucí mi přijde říkat tomu funkce, je to spíš "něco jako" enum s dodatečnými daty (ostatně ve Swiftu to je oficiálně enum). Pak se taky lépe vysvětluje pattern matching.
U té správnosti si nejsem jistý, protože podrobně neznám Haskell, třeba to v něm funkce je, ale obecně to v FP neplatí.
-
Matoucí mi přijde říkat tomu funkce, je to spíš "něco jako" enum s dodatečnými daty (ostatně ve Swiftu to je oficiálně enum). Pak se taky lépe vysvětluje pattern matching.
U té správnosti si nejsem jistý, protože podrobně neznám Haskell, třeba to v něm funkce je, ale obecně to v FP neplatí.
Mně právě přijde rozumný se na to dívat jako na funkci - nevím o tom, že by někde šla použít funkce a ne data contructor (tím neříkám, že to neexistuje, jenom o tom nevím). U speciálního typu i Haskell wiki mluví o funkci:
Smart constructors are just functions
https://wiki.haskell.org/Smart_constructors
Dalo by se data konstruktor od normální funkce odlišit, ale moc nevím, k čemu by to bylo dobrý. Naopak neodlišovat to mi přijde fakt elegantní, páč pak můžeš s klidem udělat třeba:
Prelude> map Just [1,2,3]
[Just 1,Just 2,Just 3]
Teď z hlavy si neumím vybavit všechny souvislosti, ale třeba ty monády by se asi ještě zkomplikovaly, kdybys tu distinkci dělal.
-
Nebo:
Value constructors are actually functions
http://learnyouahaskell.com/making-our-own-types-and-typeclasses
-
Matoucí mi přijde říkat tomu funkce, je to spíš "něco jako" enum s dodatečnými daty (ostatně ve Swiftu to je oficiálně enum). Pak se taky lépe vysvětluje pattern matching.
U té správnosti si nejsem jistý, protože podrobně neznám Haskell, třeba to v něm funkce je, ale obecně to v FP neplatí.
Mně právě přijde rozumný se na to dívat jako na funkci - nevím o tom, že by někde šla použít funkce a ne data contructor (tím neříkám, že to neexistuje, jenom o tom nevím). U speciálního typu i Haskell wiki mluví o funkci:
Smart constructors are just functions
https://wiki.haskell.org/Smart_constructors
Dalo by se data konstruktor od normální funkce odlišit, ale moc nevím, k čemu by to bylo dobrý. Naopak neodlišovat to mi přijde fakt elegantní, páč pak můžeš s klidem udělat třeba:
Prelude> map Just [1,2,3]
[Just 1,Just 2,Just 3]
Teď z hlavy si neumím vybavit všechny souvislosti, ale třeba ty monády by se asi ještě zkomplikovaly, kdybys tu distinkci dělal.
Nechci se pouštět do diskuze, fakt Haskell moc neznám. Ale z určitého pohledu to smysl dává, ten poslední příklad je celkem hezký a vlastně to třeba v tom Swiftu píšu stejně de facto podvědomě, aniž bych se nad tím pozastavoval. Za ten příklad u mě máš, jak říkával můj profesor fyziky, "malé bezvýznamné plus" ;)
-
Nebo:
Value constructors are actually functions
http://learnyouahaskell.com/making-our-own-types-and-typeclasses
Když na tím teď tak přemýšlím, tak mě zarazilo, že tomu říkáš endofunktor, ten by měl operovat na jiné úrovni, ale třeba jsem to jen blbě pochopil.
-
Když na tím teď tak přemýšlím, tak mě zarazilo, že tomu říkáš endofunktor, ten by měl operovat na jiné úrovni, ale třeba jsem to jen blbě pochopil.
No to máš asi pravdu, to byl ukvapenej závěr, jehož problematičnost jsem si hned uvědomil a chtěl poladit tím editem, ale ani tak si nejsem jistej, jestli jsem nenapsal blbost ;)
Pokud bysme měli kategorii haskellovských typů, tak funkce jsou morfismy a datakonstruktorem D můžeš mapovat
f: a -> b
na
f: D a -> D b
takže funktor to je a protože i (D a) je haskellovský typ, tak by to měl být endofunktor.
...ovšem pokud přijmeme, že konstruktor je normální funkce, tak se nám to trochu pomotá a nevím, jestli by to pak takhle šlo říct, protože tímpádem by to byl morfismus.
No, hele, s tím si musíš poradit spíš ty, pro mě je to na pokraji mých znalostí ;)
-
Když na tím teď tak přemýšlím, tak mě zarazilo, že tomu říkáš endofunktor, ten by měl operovat na jiné úrovni, ale třeba jsem to jen blbě pochopil.
No to máš asi pravdu, to byl ukvapenej závěr, jehož problematičnost jsem si hned uvědomil a chtěl poladit tím editem, ale ani tak si nejsem jistej, jestli jsem nenapsal blbost ;)
Pokud bysme měli kategorii haskellovských typů, tak funkce jsou morfismy a datakonstruktorem D můžeš mapovat
f: a -> b
na
f: D a -> D b
takže funktor to je a protože i (D a) je haskellovský typ, tak by to měl být endofunktor.
...ovšem pokud přijmeme, že konstruktor je normální funkce, tak se nám to trochu pomotá a nevím, jestli by to pak takhle šlo říct, protože tímpádem by to byl morfismus.
No, hele, s tím si musíš poradit spíš ty, pro mě je to na pokraji mých znalostí ;)
Pořád si myslím, že mícháš dvě různé věci. "CiselnaHodnota" nebo něco takového je morfismus, ale funktory se aplikují na morfismy, ne hodnoty. Každopádně tomu asi rozumíme oba a jen se trochu popletla terminologie.
-
ale funktory se aplikují na morfismy, ne hodnoty. Každopádně tomu asi rozumíme oba a jen se trochu popletla terminologie.
No to sice jo, ale když máš to f: D a -> D b, tak jsi vlastně f (tj. morfismus) liftnul z jedné kategorie ({a,b}) do druhé ({D a, D b}). Nejsem si jistej, jestli je korektní říct o D samotném, že je to funktor. Spíš by na to musel být ještě jiný symbol, označující tu vlastní operaci f -> D(f). Každopádně v Haskellu se functor říká té vlastní věci, která se mapuje přes hodnoty:
class Functor f where
fmap :: (a -> b) -> f a -> f b
Ale nevylučuju, že to teď motám, momentálně mám v hlavě spíš to, že jsem si koupil http://store.steampowered.com/app/319630/ a nemůžu se dočkat, až si to zahraju ;)
-
ale funktory se aplikují na morfismy, ne hodnoty. Každopádně tomu asi rozumíme oba a jen se trochu popletla terminologie.
No to sice jo, ale když máš to f: D a -> D b, tak jsi vlastně f (tj. morfismus) liftnul z jedné kategorie ({a,b}) do druhé ({D a, D b}). Nejsem si jistej, jestli je korektní říct o D samotném, že je to funktor. Spíš by na to musel být ještě jiný symbol, označující tu vlastní operaci f -> D(f). Každopádně v Haskellu se functor říká té vlastní věci, která se mapuje přes hodnoty:
class Functor f where
fmap :: (a -> b) -> f a -> f b
Ale nevylučuju, že to teď motám, momentálně mám v hlavě spíš to, že jsem si koupil http://store.steampowered.com/app/319630/ a nemůžu se dočkat, až si to zahraju ;)
Teď jsi to zamotal ještě víc :) Kategorii máme přece jen jednu. Mně šlo jen o to, že to D funktor není.
-
Kategorii máme přece jen jednu.
No ano, ale je to prostě mapování odněkud někam a mapuješ funkce. I když prakticky to je aplikace na hodnoty, což je matoucí.
No ať je to jak chce, jdu konečně hrát, hezký zbytek večera ;)
-
Kategorii máme přece jen jednu.
No ano, ale je to prostě mapování odněkud někam a mapuješ funkce. I když prakticky to je aplikace na hodnoty, což je matoucí.
No ať je to jak chce, jdu konečně hrát, hezký zbytek večera ;)
Já to radši shrnu, ať to nějak rozmotáme (tím "liftnutím" se to zamotalo příliš). Mějme
protocol Boxable {}
enum Box {
case Some(Boxable)
}
extension Int : Boxable {}
let list = [1, 2, 3].map(Box.Some)
print(list)Na Box.Some se dá dívat jako na morfismus z Boxable na Box, což jsou objekty naší jediné kategorie. Funktor (každý je endofunktorem) může mapovat třeba právě Boxable na Box nebo Box.Some na něco, ale samotné Box.Some funktorem není (v kategorii typů, v Haskellu tedy Hask).
P.S. Užij si hraní ;)
-
Máš pravdu, náčelníku. Functor není ten konstruktor, ale typ, který je pomocí něho definovaný:
instance Functor Maybe where
fmap _ Nothing = Nothing
fmap f (Just a) = Just (f a)
Parádně jsem se v tom zamotal, po ránu s čistou hlavou to jde líp :))
-
Tak další praktický příklad strictness vs. laziness. Potřebuju na kliknutí vybrat nějakou barvu a přiřadit ji nějaké kresbě. Kreseb může být víc. Když odkliknu a kliknu, rád bych, aby tam byla stejná barva. V lazy haskellu:
let colors = map genColor [1..]A je to. Můžu to dát do IORefu nebo nějakého stavu.
atomicModifyIORef ioref ((,) <$> drop 1 <*> head) je následující barva. Když uživatel odklikne, vyměním stav na
modifyIORef ioref (puvodni_barva :)"A je to. Jenže to potřebuju udělat ve striktním purescriptu... a než abych se zabýval, jak to udělat lazy listem, tak asi zvolím nějaké "náhradní" řešení (typu konečný seznam barev).
Ne, že by to člověk potřeboval často, ale občas se to prostě hodí... a explicitní thunkování není úplně příjemné na pohled.
-
Ne, že by to člověk potřeboval často, ale občas se to prostě hodí...
To je ovšem jádro problému - za něco, co není potřeba příliš často, platíte velmi vysokou cenu. A navíc je obtížnější to použít s vedlejšími efekty, např. v IO monádě:
-- ideálně bychom nechtěli počítat x a y, pokud se nepoužijí uvnitř case, zde se ovšem spočítají
x <- necoSloziteho a1 a2 a3
y <- necoSloziteho2 x a4 a5 a6 -- závisí na x
case neco of
A -> -- použije se y
B -> -- nepoužije se x ani y
C -> -- použije se x (y nehceme počítat)
D -> -- použije se y
Například ve Scale to jde snadno zařídit, aby se x a y nepočítaly, pokud nejsou použity:
lazy val x = necoSloziteho(a1, a2, a3)
lazy val y = necoSloziteho2(x, a4, a5, a6) // závisí na x
neco match {
case A => // použije se y
case B => // nepoužije se x ani y
case C => // použije se x (y nehceme počítat)
case D => // použije se y
}
-
Ne, že by to člověk potřeboval často, ale občas se to prostě hodí...
To je ovšem jádro problému - za něco, co není potřeba příliš často, platíte velmi vysokou cenu. A navíc je obtížnější to použít s vedlejšími efekty, např. v IO monádě:
-- ideálně bychom nechtěli počítat x a y, pokud se nepoužijí uvnitř case, zde se ovšem spočítají
x <- necoSloziteho a1 a2 a3
y <- necoSloziteho2 x a4 a5 a6 -- závisí na x
case neco of
A -> -- použije se y
B -> -- nepoužije se x ani y
C -> -- použije se x (y nehceme počítat)
D -> -- použije se y
Například ve Scale to jde snadno zařídit, aby se x a y nepočítaly, pokud nejsou použity:
lazy val x = necoSloziteho(a1, a2, a3)
lazy val y = necoSloziteho2(x, a4, a5, a6) // závisí na x
neco match {
case A => // použije se y
case B => // nepoužije se x ani y
case C => // použije se x (y nehceme počítat)
case D => // použije se y
}
to "lazy" nějak memoizuje?
-
to "lazy" nějak memoizuje?
Ano.
-
to "lazy" nějak memoizuje?
Ano.
jak se to liší od let + unsafePerformIO?
-
jak se to liší od let + unsafePerformIO?
Například nehrozí, že si tím otevřete díru do typového systému (s pomocí unsafePerformIO můžete napsat coerce) - na rozdíl od jazyků z rodiny ML nemá Haskell value restriction nebo jiný podobný mechanismus, který by tomu zabránil. Ve Scale jsou zase všechny hodnoty monomorfní - problém tam také nehrozí.
-
jak se to liší od let + unsafePerformIO?
Neliší. Je to stejná prasárna. Znamená to "netuším, kdy se tyhle efekty provedou a je mi to jedno". I s unsafePerformIO (nebo unsafeInterleaveIO) se člověk musí snažit, aby ten coerce napsal, o to nejde. Jde o to, že člověk neví, kdy se efekty provedou. V Haskellu existuje věc jménem "getContent" pro čtení ze souboru, která je lazy. V produkčních programech by se neměla ani náhodou objevit, protože přestože vypadá "referenčně transparentní", tak v momentě, kdy si ten "file handle" zavřete, tak najednou přestane být.
Výsledkem pak je, že pokud se někde v tomhle "lazy" kódu stane výjimka, tak je člověk pak překvapený, kde mu ta výjimka vyskakuje....
Například ve Scale to jde snadno zařídit, aby se x a y nepočítaly, pokud nejsou použity:
lazy val x = necoSloziteho(a1, a2, a3)
lazy val y = necoSloziteho2(x, a4, a5, a6) // závisí na x
neco match {
case A => // použije se y
case B => // nepoužije se x ani y
case C => // použije se x (y nehceme počítat)
case D => // použije se y
}
Tohle ale není problém:
x <- necoSloziteho a1 a2 a3
let ry = necoSloziteho2 x a4 a5 a6 -- závisí na x
case neco of
A -> do
y <- ry-- použije se y
..
B -> -- nepoužije se x ani y
C -> -- použije se x (y nehceme počítat)
D -> do
y <- ry -- použije se y
...
Tak vlastně kýženého efektu dosáhnu a je zcela jasné, KDE se y s vedlejšími efekty použilo. Pokud chci přesně vědět a specifikovat, kde to y použiju, tak to asi jinak nejde - a mě se to moc líbí :)
-
Aha, už to vidím.... ve větvi C je nepoužije se ani jedno... jo, to by se muselo nějak vymyslet "jinak", explicitně.
Scalu neznám, IMO rozdíl oproti unsafePerformIO bude, že tohle bude fungovat jenom ve scope té funkce, aby ta laziness právě nemohla "utéct" moc daleko. No, ale když už jsme u toho použití, tak tohle jsem zatím nepotřeboval, laziness v pure kódu využívám přece jen častěji...
-
Fakt nechapu co tu resite...
monada je JEN sekvence operaci, NIC JINEHO. Je to zpusob jak ve funkcionalnim jazyku popsat, ze abych z hodnoty y spocital z, potrebuju k tomu hodnotu x
-
Nemá budoucnost je to jen teoretický jazyk, který se v praxi nedá použít.
-
Nemá budoucnost je to jen teoretický jazyk, který se v praxi nedá použít.
Slyšeli jsme slovo iksperta :)
-
Nemá budoucnost je to jen teoretický jazyk, který se v praxi nedá použít.
Slišeli sme slovo buzeranta :)
A taky slovo alkoholika.
-
Vyvoj dnes nemozes robit sam. Software musi mat nejaku kontinuitu. No a na Haskel nikoho rozumneho nenahovoris.
-
Fakt nechapu co tu resite...
monada je JEN sekvence operaci, NIC JINEHO. Je to zpusob jak ve funkcionalnim jazyku popsat, ze abych z hodnoty y spocital z, potrebuju k tomu hodnotu x
Kde vidíš u monád Maybe nebo Either sekvenci?
-
Nemá budoucnost je to jen teoretický jazyk, který se v praxi nedá použít.
Škoda, že se i používá... ale hlavně pro vývojáře rozhodně není na škodu, když si jej osahá... minimálně si rozšíří obzory :)
Vyvoj dnes nemozes robit sam. Software musi mat nejaku kontinuitu. No a na Haskel nikoho rozumneho nenahovoris.
aha
-
Fakt nechapu co tu resite...
monada je JEN sekvence operaci, NIC JINEHO. Je to zpusob jak ve funkcionalnim jazyku popsat, ze abych z hodnoty y spocital z, potrebuju k tomu hodnotu x
Kde vidíš u monád Maybe nebo Either sekvenci?
sekvence výpočtů, které můžou selhat?
-
Fakt nechapu co tu resite...
monada je JEN sekvence operaci, NIC JINEHO. Je to zpusob jak ve funkcionalnim jazyku popsat, ze abych z hodnoty y spocital z, potrebuju k tomu hodnotu x
Kde vidíš u monád Maybe nebo Either sekvenci?
sekvence výpočtů, které můžou selhat?
Tak takhle možná jo. To pak je sekvence i inc(inc(inc(inc(1))) == 5Jako proč ne. Ale nevidím to v tom.
-
monada je JEN sekvence operaci, NIC JINEHO.
To je jenom jedna z mnoha predstavitelnych monad. Tohle tvoje tvrzeni je stejne nepravdive jako tvrzeni "grupa neni nic jineho nez scitani".
Je to zpusob jak ve funkcionalnim jazyku popsat, ze abych z hodnoty y spocital z, potrebuju k tomu hodnotu x
Na to nepotrebujes monadu, to se da udelat daleko jednoduseji:
z = f(x,g(y))
Ve skutecnosti tahle konkretni monada, o ktere mluvis, slouzi k tomu, aby se vynutilo poradi vypoctu v jazyce, ktery je lazy, a timpadem poradi vypoctu sam o sobe negarantuje.
-
Má Haskell budoucnost?
Poloz si otazku na aky ucel by mal mat buducnost?
Na enterprise programovanie vacsich veci sa pouziva Java, C++, COBOL, C#, ... Ostatne jazyky sluzia na rychle naprogramovanie malych uzitocnych veci - tzv. skriptovanie. Podla mna s Haskellom to bola hlavne modna vlna. Haskell ma buducnost asi nadalej v akademickej sfere na vyuku FP, ale v praxi moc nie. Co sa tyka FP, tak pre prax je lepsi napr. OCAML, lebo sa v tom da programovat aj normalne (imperativne), alebo klasicke jazyky zalozene na LISP/Scheme: racket, newlisp...
Pripada mi to dost podobne ako ked prisiel Ruby a vsetci hovorili, ze je o moc lepsi ako Perl a Python. Naucil som sa ho sice - pacil sa mi, lebo mal regex ala Perl a nebolo treba odsadzovat ala Python - ale zial nakoniec v praxi som ho pouzil asi iba raz. Ukazalo sa ze Perl a Python pouzivam aj nadalej a Ruby ich nenahradil.
Haskell som skusal, ale nie je mi moc sympaticka ta syntax - povinne odsadzovane ako v Pythone... Viac sa mi paci OCaml, alebo klasicke lispovske Racket alebo NewLisp.
Preto si myslim, ze Haskell nebude tiez nejaky dlhotrvajuci trhak. Urcite ma zmysel naucit sa ho do urcitej miery , aby clovek ziskal rozhlad, ale to je asi vsetko ... dobre platenu pracu v nom asi neziskas.
-
Na enterprise programovanie vacsich veci sa pouziva Java, C++, COBOL, C#, ... Ostatne jazyky sluzia na rychle naprogramovanie malych uzitocnych veci - tzv. skriptovanie.
Chybi ti tam Go.
Podla mna s Haskellom to bola hlavne modna vlna.
Kdy? Pred 28 lety, kdy vznikl? :)
-
monada je JEN sekvence operaci, NIC JINEHO.
To je jenom jedna z mnoha predstavitelnych monad. Tohle tvoje tvrzeni je stejne nepravdive jako tvrzeni "grupa neni nic jineho nez scitani".
To je nesmyslná formulace, grupa je vždy nějaká množina s nějakou signaturou splňující určité podmínky, nelze říct, že binární operace grupy je grupa.
-
Fakt nechapu co tu resite...
monada je JEN sekvence operaci, NIC JINEHO. Je to zpusob jak ve funkcionalnim jazyku popsat, ze abych z hodnoty y spocital z, potrebuju k tomu hodnotu x
Kde vidíš u monád Maybe nebo Either sekvenci?
sekvence výpočtů, které můžou selhat?
Každá monáda má bind, takže jde řetězit, ale nevím, jestli je vhodné ji takto definovat, tady jde spíš o zákonitosti, které to řetězení zachovává (především asociativita).
-
To je nesmyslná formulace, grupa je vždy nějaká množina s nějakou signaturou splňující určité podmínky, nelze říct, že binární operace grupy je grupa.
A přesně proto jsem to přesně takhle napsal - protože to tvrzení, na který jsem reagoval, je blbě napsaný a když si domyslíš smysl, tak je nepravdivý.
(https://cdn4.iconfinder.com/data/icons/education-flat-9/614/1080_-_Bulb-128.png)
-
To je nesmyslná formulace, grupa je vždy nějaká množina s nějakou signaturou splňující určité podmínky, nelze říct, že binární operace grupy je grupa.
A přesně proto jsem to přesně takhle napsal - protože to tvrzení, na který jsem reagoval, je blbě napsaný a když si domyslíš smysl, tak je nepravdivý.
(https://cdn4.iconfinder.com/data/icons/education-flat-9/614/1080_-_Bulb-128.png)
Nemyslím, v minulosti jsi už vícekrát použil naprosto vážně frázi “sčítání je grupa.” Akorát nevím, jestli si to fakt myslíš, nebo to je jen zkratkovité uvažování.
-
Nemyslím, v minulosti jsi už vícekrát použil naprosto vážně frázi “sčítání je grupa.” Akorát nevím, jestli si to fakt myslíš, nebo to je jen zkratkovité uvažování.
Sčítání je grupa, ale grupa není sčítání.
-
Na Haskelli bezi Cardano a uvadzaju to ako jednu z vyhod oproti inym kryptomenam.
-
Nemyslím, v minulosti jsi už vícekrát použil naprosto vážně frázi “sčítání je grupa.” Akorát nevím, jestli si to fakt myslíš, nebo to je jen zkratkovité uvažování.
Sčítání je grupa, ale grupa není sčítání.
To je blbost, taky se v tom nevyznáš. Co třeba přirozená čísla se sčítáním, to grupa není.
-
Nemyslím, v minulosti jsi už vícekrát použil naprosto vážně frázi “sčítání je grupa.”
Jo, asi jednou nebo dvakrát jako zkratku, než jsem pochopil, že se tady vždycky najde nějakej tydýt, kterej sice smysl chápe, ale nemůže si nechat ujít žádnou příležitost nahonit si triko...
Od té doby už píšu radši https://forum.root.cz/index.php?topic=15324.msg209596#msg209596 Tentokrát jsem to napsal fakt schválně a nezklamals...
Akorát nevím, jestli si to fakt myslíš, nebo to je jen zkratkovité uvažování.
Můžu si k tobě zajít lehnout na lehátko, proberem to, třeba mě v mládí mlátil otec novinama. Hypnóza nutná není, ale bodla by.
-
Nemyslím, v minulosti jsi už vícekrát použil naprosto vážně frázi “sčítání je grupa.”
Jo, asi jednou nebo dvakrát jako zkratku, než jsem pochopil, že se tady vždycky najde nějakej tydýt, kterej sice smysl chápe, ale nemůže si nechat ujít žádnou příležitost nahonit si triko...
Od té doby už píšu radši https://forum.root.cz/index.php?topic=15324.msg209596#msg209596 Tentokrát jsem to napsal fakt schválně a nezklamals...
Akorát nevím, jestli si to fakt myslíš, nebo to je jen zkratkovité uvažování.
Můžu si k tobě zajít lehnout na lehátko, proberem to, třeba mě v mládí mlátil otec novinama. Hypnóza nutná není, ale bodla by.
Nemusíš hned urážet, ono přesné vyjadřování je poměrně důležité, aspoň teda když jde o matiku, jinak se diskuse vždy zvrtne, protože dochází k nedorozuměním.
-
monada je JEN sekvence operaci, NIC JINEHO.
To je jenom jedna z mnoha predstavitelnych monad. Tohle tvoje tvrzeni je stejne nepravdive jako tvrzeni "grupa neni nic jineho nez scitani".
To je nesmyslná formulace, grupa je vždy nějaká množina s nějakou signaturou splňující určité podmínky, nelze říct, že binární operace grupy je grupa.
Tady máš názornou ukázku toho, že ani přesné vyjadřování nepomáhá, když nechápeš psaný text, který není o grupách, ale o monádách.
-
Nemusíš hned urážet,
Nefandi si :)
ono přesné vyjadřování je poměrně důležité, aspoň teda když jde o matiku, jinak se diskuse vždy zvrtne, protože dochází k nedorozuměním.
Jo, ale při hovoru s inteligentníma lidma se často používají zkratky a teprve když si lidi nerozumí, tak se zeptají a doupřesní. Oproti tomu mezi anonymama je běžný, že si lidi začnou honit triko na krávovině.
Přesný vyjadřování je potřeba, ale ne samoúčelně a ne proto, aby člověk ukojil fašisty. Viz:
Každá monáda má bind
Fakt? "Monáda má bind"? Není to spíš tak, že monáda je nějaká struktura, která má nějaké vlastnosti a jedním ze způsobů, jak monádu definovat, je (kromě jiného) bind?
takže jde řetězit
Monáda "jde řetězit"? Fakt? V jakém smyslu? Co nejde řetězit? Opravdu jde řetězit monáda?
ale nevím, jestli je vhodné ji takto definovat,
Já to vím: není vhodné ji takto definovat, protože to že "něco jde zřetězit" není žádná definice, natož monády.
tady jde spíš o zákonitosti, které to řetězení zachovává (především asociativita).
"Především"? Proč? "Monad laws" jsou (v obvyklém znění) tři a všechny musí být splněny. Proč by asociativita měla být důležitější než jiné?
Vidíš, honit si triko na kokotinách je fakt lehký :)
-
Nemusíš hned urážet,
Nefandi si :)
ono přesné vyjadřování je poměrně důležité, aspoň teda když jde o matiku, jinak se diskuse vždy zvrtne, protože dochází k nedorozuměním.
Jo, ale při hovoru s inteligentníma lidma se často používají zkratky a teprve když si lidi nerozumí, tak se zeptají a doupřesní. Oproti tomu mezi anonymama je běžný, že si lidi začnou honit triko na krávovině.
Přesný vyjadřování je potřeba, ale ne samoúčelně a ne proto, aby člověk ukojil fašisty. Viz:
Každá monáda má bind
Fakt? "Monáda má bind"? Není to spíš tak, že monáda je nějaká struktura, která má nějaké vlastnosti a jedním ze způsobů, jak monádu definovat, je (kromě jiného) bind?
takže jde řetězit
Monáda "jde řetězit"? Fakt? V jakém smyslu? Co nejde řetězit? Opravdu jde řetězit monáda?
ale nevím, jestli je vhodné ji takto definovat,
Já to vím: není vhodné ji takto definovat, protože to že "něco jde zřetězit" není žádná definice, natož monády.
tady jde spíš o zákonitosti, které to řetězení zachovává (především asociativita).
"Především"? Proč? "Monad laws" jsou (v obvyklém znění) tři a všechny musí být splněny. Proč by asociativita měla být důležitější než jiné?
Vidíš, honit si triko na kokotinách je fakt lehký :)
Ty jsi skvělý příklad Dunning-Krugerova efektu. Hotové Ovčáčkovo dvojče. Nerozmnožuj se, ať Idiokracie zůstane jen filmem.
-
Nemá budoucnost je to jen teoretický jazyk, který se v praxi nedá použít.
::)
-
Nemá budoucnost je to jen teoretický jazyk, který se v praxi nedá použít.
::)
j, a prto ho hlavne pouzivaji banky:D ...
-
Nemá budoucnost je to jen teoretický jazyk, který se v praxi nedá použít.
::)
j, a prto ho hlavne pouzivaji banky:D ...
Banky dnes používají hlavně Javu, dříve COBOL.
-
banky a Haskell ? A naco by im tam bol ?...
V bankach sa pouzivaju databaze a na pracu s databazou su najlepsie COBOL + Java.
-
Ve vlákně o Pythonu se rozhořela vášnivá debata o Haskellu. S tímto jazykem teprve začínám a zatím narážím na obtížnou čitelnost některých zdrojáků. Vím, že si na to časem zvyknu, ale přesto se ptám: Má Haskell budoucnost?
IMHO Haskell je vhodny hlavne na vyuku. Mimo toho moc velku buducnost nema. Python ma o moc vacsiu buducnost ako Haskell.
-
Ve vlákně o Pythonu se rozhořela vášnivá debata o Haskellu. S tímto jazykem teprve začínám a zatím narážím na obtížnou čitelnost některých zdrojáků. Vím, že si na to časem zvyknu, ale přesto se ptám: Má Haskell budoucnost?
IMHO Haskell je vhodny hlavne na vyuku. Mimo toho moc velku buducnost nema. Python ma o moc vacsiu buducnost ako Haskell.
neblábol
-
neblábol
Nemá budoucnost je to jen teoretický jazyk, který se v praxi nedá použít.
::)
No vztekáte se hezky, ale macík a kit mají z větší části pravdu. Haskell v akademické oblastí začal a nikdy se od ní moc neodloučil. Přišel s konceptem lazy evaluation všude a potom 25 let hledá k čemu je to vlastně dobré.
Připomíná mi to různé alternativní medicíny. Haskell má taky spoustu nadšených evangelistů tvrdících jak je to nejlepší věc na zeměkouli, ale přitom mu chybí výsledky. Mezitím polofunkcionální jazyky jako Scala dokáží generovat násobně více zájmu a projektů (a samozřejmě peněz).
-
Haskell v akademické oblastí začal a nikdy se od ní moc neodloučil.
Coz nemusi byt vubec spatne, pokud to neni jeho primarni ambice. Aneb, trochu se zacyklujes :) https://forum.root.cz/index.php?topic=13225.msg166040#msg166040
Přišel s konceptem lazy evaluation všude a potom 25 let hledá k čemu je to vlastně dobré.
K cemu je to dobre, je jasny. Spis se hledaji (programmer-friendly) zpusoby, jak pri zachovani laziness a purity psat programy s efekty.
Připomíná mi to různé alternativní medicíny. Haskell má taky spoustu nadšených evangelistů tvrdících jak je to nejlepší věc na zeměkouli, ale přitom mu chybí výsledky. Mezitím polofunkcionální jazyky jako Scala dokáží generovat násobně více zájmu a projektů (a samozřejmě peněz).
Zamlceny, ne nutny predpoklad "vysledky = zajem".
Haskell je proste pro Bezneho Frantu Programatora nezvykly, neintuitivni, slozity a obtizne pochopitelny. Cili malo kdo ma nervy na to, aby vubec zjistil, jestli skutecne je nebo neni dobry - ztrati totiz zajem driv, nez se ho nauci dostatecne na to, aby to mohl posoudit. To ale nic nemeni na tom, ze koho programovaci jazyky zajimaji a intelektualne na to ma, ten se ho nauci a inspiraci z nej treba prenese do jinych jazyku. To je samo o sobe imho dobry vysledek - i kdyby mel Haskell jenom hrat roli laboratore, ze ktere se sem tam neco prenese do praktictejsich jazyku typu Elm, Scala, Elixir, Rust, F#..., proc ne?
-
koho programovaci jazyky zajimaji a intelektualne na to ma,
::)
-
koho programovaci jazyky zajimaji a intelektualne na to ma,
::)
To je prostě konstatování faktu. Haskell je prostě náročnější než Python nebo Javascript. A je to jeho nevýhoda.
-
Pokud jsou ambice Haskellu aby to byl proof of concept jazyk a laboratoř nápadů pro ostatní tak je vše v pořádku. Ale z diskuzí tady na rootu a jinde mi příjde, že je to kouzelná hůlka co každý kód zelegantní a zparalerizuje a jediné co ho drží zpátky jsou cizí pikle a lopaty co tomu nerozumí.
Jinak představa, že hlavní důvod nerozšířenosti Haskellu je, že na něj má dostatečný mozkový výkon jen malá, elitní část populace je docela úsměvná. Nedivim se, že se tahle myšlenka, uchytila zrovna v téhle komunitě ::)
-
Pokud jsou ambice Haskellu aby to byl proof of concept jazyk a laboratoř nápadů pro ostatní tak je vše v pořádku. Ale z diskuzí tady na rootu a jinde mi příjde, že je to kouzelná hůlka co každý kód zelegantní a zparalerizuje a jediné co ho drží zpátky jsou cizí pikle a lopaty co tomu nerozumí.
Ona je pravda nekde mezi. (Btw - ty "pikle" to je straw man, nebo jsi to tu _skutecne_ nekde videl?)
Jinak představa, že hlavní důvod nerozšířenosti Haskellu je, že na něj má dostatečný mozkový výkon jen malá, elitní část populace je docela úsměvná. Nedivim se, že se tahle myšlenka, uchytila zrovna v téhle komunitě ::)
Je to jeden z duvodu. Ne jediny, ale jeden z podstatnych.
-
Pokud jsou ambice Haskellu aby to byl proof of concept jazyk a laboratoř nápadů pro ostatní tak je vše v pořádku. Ale z diskuzí tady na rootu a jinde mi příjde, že je to kouzelná hůlka co každý kód zelegantní a zparalerizuje a jediné co ho drží zpátky jsou cizí pikle a lopaty co tomu nerozumí.
Tak to si nikdo, kdo to používá, nemyslí. Už třeba vzhledem k tomu, že to je jazyk s GC, tak prostě na různé úlohy prostě vhodný není čistě z performance důvodů. Spíš řekl, že to je velmi zajímavě navržený jazyk, který se prakticky supr používá a díky tomu, jak je navržený, tak je to zároveň skvělá laboratoř pro nové koncepty.
Třeba teď se docela zvažuje implementace lineárních typů a fakt nikdo netuší, jestli to bude úplně slepá cesta, nebo z toho něco vyleze. Počítá se s tím, že se udělá implementace a pak se uvidí, jestli to v praxi lidi začnou používat, jak to budou používat a na základě toho se ten koncept bude nějak dál rozvíjet.
Jinak představa, že hlavní důvod nerozšířenosti Haskellu je, že na něj má dostatečný mozkový výkon jen malá, elitní část populace je docela úsměvná. Nedivim se, že se tahle myšlenka, uchytila zrovna v téhle komunitě ::)
Určitá část populace prostě není schopna pochopit pointery. Ta druhá část populace nechápe, co je na tom složitého. Další část populace není schopna pochopit rekurzi. Ti, co ji chápu nerozumí, co na tom je složitého. A další část není schopna pochopit třeba tohle: https://www.schoolofhaskell.com/user/bartosz/understanding-algebras (https://www.schoolofhaskell.com/user/bartosz/understanding-algebras).... a ti, co to pochopili, chápou, že to není zas až tak lehké......
Ten jazyk se docela běžně používá způsoby, které jsou v jiných jazycích strašně obtížně vyjádřitelné. Typový systém se používá způsobem, který lze horkotěžko v jiných jazycích simulovat makrama nebo introspekcí..a nebo vůbec ne.
Když se bavím s lidmi o tom, jestli je programování složité, tak všem říkám, že je to strašně lehký. Co je těžkého na pythonu? Udělej tohle, udělej tamto...je to spíš "hodně jednoduchých věcí". Ale že je haskell lehký bych se teda fakt neodvážil. Do těch textů o katamorfismu jsem čučel asi týden, než jsem pochopil, jak to vůbec použít a další týden, než jsem pochopil, jak to funguje (a teorii kolem toho radši nechám jiným...).
-
Určitá část populace prostě není schopna pochopit pointery. Ta druhá část populace nechápe, co je na tom složitého. Další část populace není schopna pochopit rekurzi. Ti, co ji chápu nerozumí, co na tom je složitého. A další část není schopna pochopit třeba tohle: https://www.schoolofhaskell.com/user/bartosz/understanding-algebras (https://www.schoolofhaskell.com/user/bartosz/understanding-algebras).... a ti, co to pochopili, chápou, že to není zas až tak lehké......
Pointry a rekurze aspoň k něčemu jsou.
-
Typový systém se používá způsobem, který lze horkotěžko v jiných jazycích simulovat makrama nebo introspekcí..a nebo vůbec ne.
V C++ se dá "simulovat" statickým polymorfismem.
-
Určitá část populace prostě není schopna pochopit pointery. Ta druhá část populace nechápe, co je na tom složitého. Další část populace není schopna pochopit rekurzi. Ti, co ji chápu nerozumí, co na tom je složitého. A další část není schopna pochopit třeba tohle: https://www.schoolofhaskell.com/user/bartosz/understanding-algebras (https://www.schoolofhaskell.com/user/bartosz/understanding-algebras).... a ti, co to pochopili, chápou, že to není zas až tak lehké......
Pointry a rekurze aspoň k něčemu jsou.
Vazne? V COBOLu to nikdo nepotreboval, a co v tom napsali kodu..
Mne osobne pripada Haskell jednodussi nez mnohe prekombinovane Javove vynalezy. (Klasicky priklad je dependency injection.) Veci, ktere odpovidaji v Haskellu Jave, jsou v Haskellu mnohem primocarejsi. To, co je na Haskellu slozite jsou veci, ktere v Jave ekvivalent nemaji. A pravem - kdyby mely tak se z toho lidi zblazni. (Klasicky priklad jsou higher-kinded types.)
Jinak k te laziness - muzete rikat, ze je to spatne. Jenze prave diky tomu Haskell kladl takovy duraz na purity, o cemz se vzapeti ukazalo, ze to je uzitecna vlastnost. Myslim, ze dnes muzeme povazovat purity (nebo prinejmensim immutability) za prokazatelne pozitivni vlastnost, jenze ta by se nikdy neukazala, kdyby Haskell nemel lazy evaluation jako default.
Jinak pokud se chce nekdo dozvedet vic o tom, co a jak a proc vzniklo v Haskellu, doporucuji: https://www.youtube.com/watch?v=06x8Wf2r2Mc (https://www.youtube.com/watch?v=06x8Wf2r2Mc)
Z meho pohledu je Haskell celkem fajn jazyk a budoucnost urcite ma. Ale bude trvat, nez bude zcela pochopen sirsi verejnosti (a mezitim zase uz bude patrne nekde dal).
-
kdyby mely tak se z toho lidi zblazni. (Klasicky priklad jsou higher-kinded types.)
Co je na nich k zbláznění?
-
Určitá část populace prostě není schopna pochopit pointery. Ta druhá část populace nechápe, co je na tom složitého. Další část populace není schopna pochopit rekurzi. Ti, co ji chápu nerozumí, co na tom je složitého. A další část není schopna pochopit třeba tohle: https://www.schoolofhaskell.com/user/bartosz/understanding-algebras.... a ti, co to pochopili, chápou, že to není zas až tak lehké......
Pointry a rekurze aspoň k něčemu jsou.
Když to neumíš použít (nebo to prostě v tom jazyce není), tak to nepotřebuješ... ne že bych to používal všude, ale už se pár programů našlo, a když pak potřebuješ použít třeba paramorfismus, tak to najednou značně ulehčí život...
Typový systém se používá způsobem, který lze horkotěžko v jiných jazycích simulovat makrama nebo introspekcí..a nebo vůbec ne.
V C++ se dá "simulovat" statickým polymorfismem.
C++ umí polymorfismus podle typu návratové hodnoty? C++ umí dělat nějak (jakkoliv) introspekci?
Ono teda c++ toho poslední roky umí víc a víc, moc nesleduju....
Co je na nich k zbláznění?
Když se podívám, jak "elegantně" je třeba v C++ implementovaný operator overloading (nic jim nevyčítám, líp to asi nešlo), jak přehledně vypadá používání templates (obzvláště chybové hlášky....) tak bych neměl o používání HKT iluze... ono se to dost špatně přidává do jazyka, který s tím nebyl od začátku navrhován.
-
C++ umí polymorfismus podle typu návratové hodnoty? C++ umí dělat nějak (jakkoliv) introspekci?
Ono teda c++ toho poslední roky umí víc a víc, moc nesleduju....
Něco jako polymorfismus podle typu návratové hodnoty se tam udělat dá. Akorát to hodně drhne. Trik je v tom, že funkce jenom zabalí svoje parametry do pomocného objektu, který má akorát několik operátorů přetypování. A veškerá práce se pak udělá v odpovídajícím operátoru přetypování. Tu a tam se to dá i použít.
Ale s čistotou a flexibilitou Haskellovského řešení se to samozřejmě nedá srovnávat. ;)
Introspekci C++ umí. Akorát se tomu tam říká RTTI.
-
Když se podívám, jak "elegantně" je třeba v C++ implementovaný operator overloading (nic jim nevyčítám, líp to asi nešlo), jak přehledně vypadá používání templates (obzvláště chybové hlášky....) tak bych neměl o používání HKT iluze... ono se to dost špatně přidává do jazyka, který s tím nebyl od začátku navrhován.
Kde se v praxi kromě typových tříd používají HKT? Zrovna v C++ jde tohle jednoduše pomocí konceptů (které mimochodem řeší i ty debilní chybové hlášky, co blije gcc - clang je měl odjakživa čitelné, jde tedy o nekompetenci konkrétní implementace, ne jazyka jako takového). On ten typový systém v Haskellu není nijak extra silný, co by se třeba skutečně hodilo jsou typy identit.
-
když pak potřebuješ použít třeba paramorfismus, tak to najednou značně ulehčí život...
Pojem "paramorfismus" použije tak akorát někdo, kdo si chce honit ego (žeby nějaký komplex?). Přitom jde o prachsprosté reduce.
-
když pak potřebuješ použít třeba paramorfismus, tak to najednou značně ulehčí život...
Pojem "paramorfismus" použije tak akorát někdo, kdo si chce honit ego (žeby nějaký komplex?). Přitom jde o prachsprosté reduce.
Pojem "paramorfismus" použije někdo, kdo má matematický background. Pojem "reduce" je obecnější a vágnější. Spadá pod něj víc druhů morfismů, nejen ten para.
-
když pak potřebuješ použít třeba paramorfismus, tak to najednou značně ulehčí život...
Pojem "paramorfismus" použije tak akorát někdo, kdo si chce honit ego (žeby nějaký komplex?). Přitom jde o prachsprosté reduce.
Pojem "paramorfismus" použije někdo, kdo má matematický background. Pojem "reduce" je obecnější a vágnější. Spadá pod něj víc druhů morfismů, nejen ten para.
Evidentně nejen. Navíc zrovna tyhle "morfismy" nemají s matematikou mnoho společného, pochází z CS.
-
Introspekci C++ umí. Akorát se tomu tam říká RTTI.
Co jsem pochopil, tak RTTI akorát umí dotaz "je tohle typu X"? (což zrovna haskell bez typeclassy neumí...) Nebo umí i něco typu "co je v téhle třídě", takže se to dá použít třeba na nějakou automatickou JSON serializaci? (já fakt nevím)
Kde se v praxi kromě typových tříd používají HKT?
V praxi všude, kde se vyskytují slova "Functor", "Applicative", "Monad" a další, myslím, že třeba "lens" to taky využívá dost. Teď nově to začali používat v některých SQL knihovnách, z čehož mám trošku smíšené pocity (přijde mi to trošku jako hack..ale zatím jsem to prakticky nezkusil). Způsob práce s HKT je pak přes typové třídy, protože to jinak moc nejde, takže moc nechápu tu otázku.
Zrovna v C++ jde tohle jednoduše pomocí konceptů
...což je poměrně nový "koncept" pravděpodobně inspirovaný typeclassama z haskellu..? (jestli to dobře chápu...)
Pojem "paramorfismus" použije tak akorát někdo, kdo si chce honit ego (žeby nějaký komplex?). Přitom jde o prachsprosté reduce.
Tak já to používám nikoliv proto, že bych měl matematický background, ale protože ten termín používali lidi v těch článcích, tak se držím názvosloví... akorát, že já si teda pod pojmem "praschsprostá redukce" nepředstavím funkci typu "(f a -> a) -> Fix f -> a". Ale možná že 90% programátorů jo, a ta idea, že tohle není úplně jednoduchý téma na pochopení je vlastně jenom moje vlastní neschopnost, přiznávám, že když jsem tu signature viděl poprvé, tak jsem vůbec nechápal, jak něco takovýho může fungovat....
-
(f a -> a) -> Fix f -> aBtw, není tohle praktické použití HKT? A dokonce bez typeclass?
-
Způsob práce s HKT je pak přes typové třídy, protože to jinak moc nejde
To je jen jedno z omezení Haskellu. Nicméně časem třeba jeho typový systém vylepší.
-
(f a -> a) -> Fix f -> aBtw, není tohle praktické použití HKT? A dokonce bez typeclass?
A odpovídám si: praktické je, ale ne bez typeclass, protože mi tam chybí "Functor f" constraint...
-
Způsob práce s HKT je pak přes typové třídy, protože to jinak moc nejde
To je jen jedno z omezení Haskellu. Nicméně časem třeba jeho typový systém vylepší.
:o
-
(f a -> a) -> Fix f -> aBtw, není tohle praktické použití HKT? A dokonce bez typeclass?
To není HKT. Jaký má být kind?
-
(f a -> a) -> Fix f -> aBtw, není tohle praktické použití HKT? A dokonce bez typeclass?
A odpovídám si: praktické je, ale ne bez typeclass, protože mi tam chybí "Functor f" constraint...
To taky, ale nechtěl jsem slovíčkařit.
-
(f a -> a) -> Fix f -> aBtw, není tohle praktické použití HKT? A dokonce bez typeclass?
To není HKT. Jaký má být kind?
Kind f je "Type -> Type".
-
Způsob práce s HKT je pak přes typové třídy, protože to jinak moc nejde
To je jen jedno z omezení Haskellu. Nicméně časem třeba jeho typový systém vylepší.
:o
Co to znamená? Nemáš slov? V jiných jazycích s HKT je typový systém mnohem silnější a flexibilnější, dají se psát funkce a struktury s typovými operátory -> a =, prostě HKT bez omezení. Tím neříkám, že je to praktické, typové třídy jsou mnohdy čitelnější, ale aspoň to jde.
-
(f a -> a) -> Fix f -> aBtw, není tohle praktické použití HKT? A dokonce bez typeclass?
To není HKT. Jaký má být kind?
Kind f je "Type -> Type".
Ano. A kind "f a"? A kind "Fix f"? Celé to pak pochopitelně není HKT, stačí si to rozepsat.
-
(f a -> a) -> Fix f -> aBtw, není tohle praktické použití HKT? A dokonce bez typeclass?
To není HKT. Jaký má být kind?
Kind f je "Type -> Type".
Ano. A kind "f a"? A kind "Fix f"?
Teď nerozumím... to zdůvodnění jsem samozřejmě napsal blbě, protože celá ta funkce je:
cata :: Functor f => (f a -> a) -> Fix f -> a, kde Functor je (Type -> Type) -> Type, což je HKT.
Celé to pak pochopitelně není HKT, stačí si to rozepsat.
Ptal jsi se na pratkické použití HKT. Chceš tím říct, že výše uvedenou funkci lze implementovat bez HKT?
-
Teď nerozumím... to zdůvodnění jsem samozřejmě napsal blbě, protože celá ta funkce je:
cata :: Functor f => (f a -> a) -> Fix f -> a, kde Functor je (Type -> Type) -> Type, což je HKT.
Resp. v haskellu "(* -> *) -> Constraint", ale prostě to je HKT :)
-
No a kind Fix je:
Fix :: (* -> *) -> *což je HKT i bez typeclassy....
-
(f a -> a) -> Fix f -> aBtw, není tohle praktické použití HKT? A dokonce bez typeclass?
To není HKT. Jaký má být kind?
Kind f je "Type -> Type".
Ano. A kind "f a"? A kind "Fix f"?
Teď nerozumím... to zdůvodnění jsem samozřejmě napsal blbě, protože celá ta funkce je:
cata :: Functor f => (f a -> a) -> Fix f -> a, kde Functor je (Type -> Type) -> Type, což je HKT.
Ano, teď už to je HKT, v té haskellovské omezené formě s =>.
-
No a kind Fix je:
Fix :: (* -> *) -> *což je HKT i bez typeclassy....
Jistě, ale když se v signatuře vyskytne Fix f, tak to už HKT není, protože aplikace.
-
No a kind Fix je:
Fix :: (* -> *) -> *což je HKT i bez typeclassy....
Jistě, ale když se v signatuře vyskytne Fix f, tak to už HKT není, protože aplikace.
Otázka byla na praktické použití. Tohle není praktické použití?
V signatuře to být nemůže, protože funkce v haskellu jaksi očekávají parametr typu Type... takže v rámci haskellu tu otázku nechápu. Možná v jiných jazycích typu Agda je to možné jinak, ale tu jsem se naučit ještě nestihl (fakt to tam jde?)
-
V signatuře to být nemůže, protože funkce v haskellu jaksi očekávají parametr typu Type... takže v rámci haskellu tu otázku nechápu. Možná v jiných jazycích typu Agda je to možné jinak, ale tu jsem se naučit ještě nestihl (fakt to tam jde?)
Jistěže to jde. Tam jde věcí... A taky to jde třeba v C++.
-
V signatuře to být nemůže, protože funkce v haskellu jaksi očekávají parametr typu Type... takže v rámci haskellu tu otázku nechápu. Možná v jiných jazycích typu Agda je to možné jinak, ale tu jsem se naučit ještě nestihl (fakt to tam jde?)
Jistěže to jde. Tam jde věcí... A taky to jde třeba v C++.
No trošku to na mě dělá dojem, že si tu někdo honí ego...
Ale otázka byla na praktické použití - chceš říct, že použití "Fix f" není "praktické použití", protože to je plně aplikované..?
-
Introspekci C++ umí. Akorát se tomu tam říká RTTI.
Co jsem pochopil, tak RTTI akorát umí dotaz "je tohle typu X"? (což zrovna haskell bez typeclassy neumí...) Nebo umí i něco typu "co je v téhle třídě", takže se to dá použít třeba na nějakou automatickou JSON serializaci? (já fakt nevím)
Tohle znám pod pojmem "introspekce". Věci typu "Co je v téhle třídě?" znám pod pojmem "reflexe". Reflexi C++ neumí, ale momentálně se na statické reflexi intenzivně pracuje. Jestli se to stihne do c++20 je ve hvězdách.
-
chceš říct, že použití "Fix f" není "praktické použití", protože to je plně aplikované..?
Může být praktické, jen to není HKT.
-
chceš říct, že použití "Fix f" není "praktické použití", protože to je plně aplikované..?
Může být praktické, jen to není HKT.
Fix není HKT?
-
Kde se v praxi kromě typových tříd používají HKT?
Příklad:
cata :: Functor f => (f a -> a) -> Fix f -> a)Je použit Fix. Fix je HKT. Použití v praxi v katamorfismu. Odpovídá to na otázku?
-
Nebo ještě jednou - jinak. HKT je typ, který bere jako parametr typový konstruktor a něco z toho vyrobí. Takže třeba:
Functor :: (* -> *) -> Constraint
Fix :: (* -> *) -> *
Samozřejmě, že v kódu se to následně aplikuje celé - a konkrétně třeba u toho "Fix f" to "f" aplikované není. Ale to jaksi neznamená, že ten HKT typ "nebyl použit". Jinak IMO ty neaplikované typy nejde použít jako parametry funkcí, a tak nějak bych si dost tipnul, že to nejde v Agdě a není mi moc jasné, jak by to mohlo jít v C++. Ten typ nemá žádnou hodnotu a funkce berou hodnoty. Ale když tak suverénně píšeš, že to jde, tak se rád poučím - dáš příklady?
-
Jinak IMO ty neaplikované typy nejde použít jako parametry funkcí, a tak nějak bych si dost tipnul, že to nejde v Agdě a není mi moc jasné, jak by to mohlo jít v C++. Ten typ nemá žádnou hodnotu a funkce berou hodnoty.
Strašně to motáš, než si ujasníš terminologii, nikam se nedostanem. "Neaplikované" typy samozřejmě jdou použít jako parametry funkcí (nově i v Haskellu, když se použije příslušný jazykový přepínač). Ale díky za podnět, teď mám po nějaké době zase důvod hrát si s Haskellem a zkoušet v něm věci, které doposud šly jen v silnějších typových systémech.
-
Jinak IMO ty neaplikované typy nejde použít jako parametry funkcí, a tak nějak bych si dost tipnul, že to nejde v Agdě a není mi moc jasné, jak by to mohlo jít v C++. Ten typ nemá žádnou hodnotu a funkce berou hodnoty.
Strašně to motáš, než si ujasníš terminologii, nikam se nedostanem. "Neaplikované" typy samozřejmě jdou použít jako parametry funkcí (nově i v Haskellu, když se použije příslušný jazykový přepínač). Ale díky za podnět, teď mám po nějaké době zase důvod hrát si s Haskellem a zkoušet v něm věci, které doposud šly jen v silnějších typových systémech.
Ta funkce "cata" jde samozřejmě zavolat s TypeApplications, ale vůbec netuším, co to má společného s HKT. Ale v pohodě - tak mi to ujasni. Rád se poučím.
-
tak mi to ujasni. Rád se poučím.
Jde o tvé použití termínu "parametr funkce", to je přece ta "hodnota", kdežto v signatuře je (meta)typ. A parametrem funkce může být klidně třeba ten Fix (nejen v Agdě, i v Haskellu). Ten příslušný přepínač je KindSignatures.
-
tak mi to ujasni. Rád se poučím.
Jde o tvé použití termínu "parametr funkce", to je přece ta "hodnota", kdežto v signatuře je (meta)typ. A parametrem funkce může být klidně třeba ten Fix (nejen v Agdě, i v Haskellu). Ten příslušný přepínač je KindSignatures.
Šel by příklad? Neumím si to představit. (fakt)
-
Šel by příklad? Neumím si to představit. (fakt)
Nejjednodušší, co mě napadá:
FApp : (Type -> Type) -> Type -> Type
FApp f a = f a
Pak můžu volat např. FApp List Int.
-
Šel by příklad? Neumím si to představit. (fakt)
Nejjednodušší, co mě napadá:
FApp : (Type -> Type) -> Type -> Type
FApp f a = f a
Pak můžu volat např. FApp List Int.
No... a jaký je teda rozdíl od:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
newtype FApp :: (* -> *) -> * -> * where
FApp :: f l -> FApp f l
Resp. to, co tady celou dobu ukazuju:
newtype Fix :: (* -> *) -> * where
Fix :: f (Fix f) -> Fix f
Což je jenom rozepsaná ta původní definice?
Teda kromě toho, že haskell nemá dependent types, tak se to holt píše takhle...?
-
Šel by příklad? Neumím si to představit. (fakt)
Nejjednodušší, co mě napadá:
FApp : (Type -> Type) -> Type -> Type
FApp f a = f a
Pak můžu volat např. FApp List Int.
No... a jaký je teda rozdíl od:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
newtype FApp :: (* -> *) -> * -> * where
FApp :: f l -> FApp f l
Resp. to, co tady celou dobu ukazuju:
newtype Fix :: (* -> *) -> * where
Fix :: f (Fix f) -> Fix f
Což je jenom rozepsaná ta původní definice?
Teda kromě toho, že haskell nemá dependent types, tak se to holt píše takhle...?
Hele, tys tvrdil, že parametrem funkce nemůže být funktor. Dal jsem ti příklad, cos chtěl. Proč pořád uhýbáš?
-
Šel by příklad? Neumím si to představit. (fakt)
Nejjednodušší, co mě napadá:
FApp : (Type -> Type) -> Type -> Type
FApp f a = f a
Pak můžu volat např. FApp List Int.
No... a jaký je teda rozdíl od:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
newtype FApp :: (* -> *) -> * -> * where
FApp :: f l -> FApp f l
Resp. to, co tady celou dobu ukazuju:
newtype Fix :: (* -> *) -> * where
Fix :: f (Fix f) -> Fix f
Což je jenom rozepsaná ta původní definice?
Teda kromě toho, že haskell nemá dependent types, tak se to holt píše takhle...?
Tys tvrdil, že parametrem funkce nemůže být funktor. Dal jsem ti příklad, cos chtěl. Proč pořád uhýbáš?
V čem uhýbám? Funkce v Haskellu nemůže dostat Type jako parametr, v Agdě jsou typy first class. OK, díky za informaci, někdy se na to podívám víc.
Nicméně v Haskellu můžeš psát funkce, které akceptují typy jako parametr, akorát se to nenazývá funkce. No a tady jsem ti dal ukázku toho, že to
1) jde taky
2) Fix je příkladem takové funkce
No a ty jsi tvrdil, že cata (s Fix) není příkladem praktického užití HKT (mimo typeclassy). A mně připadá, že podle toho, co píšeš, je. Takže bys mi mohl vysvětlit, proč FApp v tvém příkladu by byl ukázkou HKT, zatímco FApp v haskellu nikoliv, případně proč je Fix něco úplně jiného. Protože mně to přijde jako dost stejné.
-
Funkce v Haskellu nemůže dostat Type jako parametr, v Agdě jsou typy first class
...
Nicméně v Haskellu můžeš psát funkce, které akceptují typy jako parametr
Koukám, že tu terminologii motáš i po explicitním vysvětlení. BTW v Haskellu jde napsat funkce typu Type->Type například.
-
Funkce v Haskellu nemůže dostat Type jako parametr, v Agdě jsou typy first class
...
Nicméně v Haskellu můžeš psát funkce, které akceptují typy jako parametr
Koukám, že tu terminologii motáš i po explicitním vysvětlení. BTW v Haskellu jde napsat funkce typu Type->Type například.
No třeba takhle:
newtype Id f = Id fAle dobře - dáš mi tvůj příklad?
-
Ale dobře - dáš mi tvůj příklad?
Na úrovni typů: Add 'Zero n = n
Add ('Succ n) m = Add n ('Succ m)
-
Ale dobře - dáš mi tvůj příklad?
Na úrovni typů: Add 'Zero n = n
Add ('Succ n) m = Add n ('Succ m)
Closed type family? Nebo obecně type families. Jasně. A použití by bylo například:
f :: Neco x -> Neco (Add x 1)
A jak se to liší od:
cata :: Functor f => (f a -> a) -> Fix f -> aProtože já bych řekl, že "Fix" je v tomhle prostě zavolání typové funkce (akorát to přes type families nejde udělat). Nebo se mýlím?
Tvrdil jsi, že použití Fix v cata není příkladem použití HKT. A to, co teď dáváš jako příklady, je tomu ekvivalentní - definice FApp přes newtype a ta Agdová dělají v podstatě to samé. Tak v čem se to teda liší? Připadá mi, že nějak...uhýbáš...?
-
BTW když to tak sleduju, vrtá mi hlavou jedna otázka na Haskelisty (a nemímím to nijak zle, žádný flame, fakt jen zvědavost)
Že je Haskell založený k obrazu CT je notoricky známý fakt. Ale vrtá mi v hlavě otázka, jestli CT pro něj funguje primárně jako design vzor, nebo jestli i nějakým netriviálním způsobem využívá "něco" (teorém, ... ) co by se bez CT odvozovalo jinak mnohem složitěji.
ufff snad je to aspoň trochu srozumitelné :)
-
BTW když to tak sleduju, vrtá mi hlavou jedna otázka na Haskelisty (a nemímím to nijak zle, žádný flame, fakt jen zvědavost)
Že je Haskell založený k obrazu CT je notoricky známý fakt. Ale vrtá mi v hlavě otázka, jestli CT pro něj funguje primárně jako design vzor, nebo jestli i nějakým netriviálním způsobem využívá "něco" (teorém, ... ) co by se bez CT odvozovalo jinak mnohem složitěji.
ufff snad je to aspoň trochu srozumitelné :)
Využívá terminologii, formální výsledky moc využívat nemůže, protože “kategorie typů” ani není kategorie podle definice v KT.
-
jestli CT pro něj funguje primárně jako design vzor, nebo jestli i nějakým netriviálním způsobem využívá "něco" (teorém, ... ) co by se bez CT odvozovalo jinak mnohem složitěji.
K vyse uvedene odpovedi bych dodal..
Haskell je spis postaveny na teorii typoveho lambda kalkulu (konkretne https://en.wikipedia.org/wiki/System_F (https://en.wikipedia.org/wiki/System_F)) nez na CT.
Nicmene, z toho malo co o CT vim, tak je obecne jen malo vysledku v CT ktere by se mimo CT dokazovaly obtizne. Hlavni smysl CT vidim v tom, ze je to jakysi "makro system" pro matematiku - muzeme stejny dukaz nechat provest pro mnoho ruznych objektu, a tim si usetrime praci. Ale samo o sobe ty dukazy nejsou magicky elegantnejsi nez by byly v kazde te jednotlive teorii.
Dusledek je, ze hodne konceptu CT se pak v Haskellu objevi jako typove tridy. Zase, jejich smyslem neni primarne poskytnout lepsi algoritmus, ale zbytecne se neopakovat.
-
objevi jako typove tridy. Zase, jejich smyslem neni primarne poskytnout lepsi algoritmus, ale zbytecne se neopakovat.
Pak je otázka, proč jsou v mainstreamu jen v C++, Swiftu a Ocamlu, neopakování se by mělo ležet na srdci hlavně lepičům v boilerplate jazycích jako Java.
-
objevi jako typove tridy. Zase, jejich smyslem neni primarne poskytnout lepsi algoritmus, ale zbytecne se neopakovat.
Pak je otázka, proč jsou v mainstreamu jen v C++, Swiftu a Ocamlu, neopakování se by mělo ležet na srdci hlavně lepičům v boilerplate jazycích jako Java.
Protoze skoro nikdo aplikace CT v programovani nezna a tak to nikomu moc nechybi. Haskell ma nektere koncepty z CT ve standardni knihovne, a i tam to nebylo uplne spravne (Monad dlouho nebyla podtridou Applicative).
Swift a OCaml zase takovy mainstream nejsou, a v C++ taky nejsou ve standardni knihovne. Takze..
Jinak Java odjakziva resila boilerplate pomoci generovani kodu prostrednictvim IDE, coz je asi ze vsech moznych ta nejhorsi metoda.
-
objevi jako typove tridy. Zase, jejich smyslem neni primarne poskytnout lepsi algoritmus, ale zbytecne se neopakovat.
Pak je otázka, proč jsou v mainstreamu jen v C++, Swiftu a Ocamlu, neopakování se by mělo ležet na srdci hlavně lepičům v boilerplate jazycích jako Java.
Protoze skoro nikdo aplikace CT v programovani nezna a tak to nikomu moc nechybi. Haskell ma nektere koncepty z CT ve standardni knihovne, a i tam to nebylo uplne spravne (Monad dlouho nebyla podtridou Applicative).
Swift a OCaml zase takovy mainstream nejsou, a v C++ taky nejsou ve standardni knihovne. Takze..
Jinak Java odjakziva resila boilerplate pomoci generovani kodu prostrednictvim IDE, coz je asi ze vsech moznych ta nejhorsi metoda.
Stačily by jen ty typové třídy, CT vzal čert, ta s nimi nesouvisí.