Rychlost Haskell vs. C++

andy

Re:Rychlost Haskell vs. C++
« Odpověď #120 kdy: 29. 08. 2018, 00:41:00 »
To je z computer science - všechny typy v haskellu obsahují bottom, no a error je funkce, která díký free theoremům má výstupní typ pouze bottom:
Jinými slovy říkáš, že error má v podstatě nedefinované chování (i když se může vyskytovat v pure kódu!) a neměl by se používat.
Přesně tak. V podstatě jediné rozumné použití je pro to říct "tady je chyba kódu". Nikoliv "toto je chyba vstupních dat". Mimochodem, to, co najdeš v snad i v ubčebnicích je, že když pak budeš mít těch errorů víc, tak je zcela nedeterministické, který z nich to nakonec vyhraje.
Tudíž rozumné použití pro "error" je: kód je špatně. Nikoliv "výjimka na základě vstupních dat, ošetříme, zareagujeme".
Citace
Pokud máš pravdu, je docela zarážející, že je to na mnoha místech v Prelude v docela zásadních funkcích:
Kód: [Vybrat]
head             :: [a] -> a
head (x:_)       =  x
head []          =  error "Prelude.head: empty list"
Není to tak trochu nebezpečné? Není to tak trochu prasárna?
Ano, je to nebezpečné, ale z úplně jiných důvodů, než které tu diskutujeme. A je to "historický dluh" ("prasárna"), už  bylo mnohokrát navrhováno, aby se tyhle funkce z Prelude vyhodily, v podstatě jediný důvod, proč tam zůstávají, je, že to je vcelku přístupné pro začátečníky (učební materiály, jednoduchost..Maybe monad fakt není pro první lekci haskellu vhodný). Spousta lidí taky používá custom Prelude, kde tyhle funkce nemají.

Nicméně důvod, proč to chtějí je úplně jiný, než to, co si asi myslíš. Ono je poměrně korektní, aby "head" zavolal "error", když ho zavoláš na prázdný list. Ty ho totiž nemáš co volat na prázdný list, takže máš chybu v kódu - a bottom je korektní výsledek. Prasárna to začne být v momentě, kdy se tu výjimku pokusíš chytit a na základě ní usuzovat něco jiného než "mám tam internal error".

Ten důvod, proč se to z Prelude snaží dostat je ale jiný: je to "partial" funkce. A Haskellisti nemají partial funkce rádi. Boří to motto "if it compiles, it works". Takže ideálně místo toho všichni používají např.:
Kód: [Vybrat]
NonEmpty a = a :| [a]
head :: NonEmpty a -> a
head (a :| _) = a

nebo

safeHead :: [a] -> Maybe a
safeHead (a:_) = Just a
safeHead [] = Nothing
No a pokud head nepoužiješ a použiješ jednu z těchhle variant, tak ti překladač je schopen garantovat, že to na prázdný list nezavoláš.


lopata

Re:Rychlost Haskell vs. C++
« Odpověď #121 kdy: 29. 08. 2018, 01:11:10 »
Přesně tak. V podstatě jediné rozumné použití je pro to říct "tady je chyba kódu". Nikoliv "toto je chyba vstupních dat". Mimochodem, to, co najdeš v snad i v ubčebnicích je, že když pak budeš mít těch errorů víc, tak je zcela nedeterministické, který z nich to nakonec vyhraje.
Tudíž rozumné použití pro "error" je: kód je špatně. Nikoliv "výjimka na základě vstupních dat, ošetříme, zareagujeme".
Ale to má Haskell udělané úplně blbě. Dokud tam budou takové prasárny (v pure kódu!), nikdy se z něj nestane normálně použitelný jazyk. Byl by problém zadefinovat, co konkrétně má error udělat? Nebyl, ale asi to není dostatečně akademické řešení... Srovnej v panic v Rustu, dělá prakticky to stejné a naprosto definovaně, dá se normálně používat: https://doc.rust-lang.org/std/macro.panic.html

andy

Re:Rychlost Haskell vs. C++
« Odpověď #122 kdy: 29. 08. 2018, 01:42:27 »
Přesně tak. V podstatě jediné rozumné použití je pro to říct "tady je chyba kódu". Nikoliv "toto je chyba vstupních dat". Mimochodem, to, co najdeš v snad i v ubčebnicích je, že když pak budeš mít těch errorů víc, tak je zcela nedeterministické, který z nich to nakonec vyhraje.
Tudíž rozumné použití pro "error" je: kód je špatně. Nikoliv "výjimka na základě vstupních dat, ošetříme, zareagujeme".
Ale to má Haskell udělané úplně blbě. Dokud tam budou takové prasárny (v pure kódu!), nikdy se z něj nestane normálně použitelný jazyk. Byl by problém zadefinovat, co konkrétně má error udělat? Nebyl, ale asi to není dostatečně akademické řešení... Srovnej v panic v Rustu, dělá prakticky to stejné a naprosto definovaně, dá se normálně používat: https://doc.rust-lang.org/std/macro.panic.html
Jaká prasárna v pure kódu? Teď ti vůbec nerozumím. Error je něco jako přístup na nulovou adresu v C. Tam je taky zadefinované (na OS s MMU), že to vygeneruje SIGSEGV. Akorát ten error se dá potom v IO chytit. Proč bys proboha něco takového chtěl "normálně" používat? To je funkce, která vůbec není určená pro "normální" použití. Nebo snad normálně používáš přístup na nulovou adresu a chytání SIGSEGV jako "normální" ošetření chyb?

lopata

Re:Rychlost Haskell vs. C++
« Odpověď #123 kdy: 29. 08. 2018, 02:00:51 »
Jaká prasárna v pure kódu? Teď ti vůbec nerozumím. Error je něco jako přístup na nulovou adresu v C. Tam je taky zadefinované (na OS s MMU), že to vygeneruje SIGSEGV. Akorát ten error se dá potom v IO chytit. Proč bys proboha něco takového chtěl "normálně" používat? To je funkce, která vůbec není určená pro "normální" použití. Nebo snad normálně používáš přístup na nulovou adresu a chytání SIGSEGV jako "normální" ošetření chyb?
Srovnej s panic v Rustu. To je funkce, která se normálně používá a funguje naprosto definovaně. Volání error v Haskellu by mohlo taky, kdyby se chtělo. Akademikům se nechce, místo toho aby zadefinovali, co přesně volání error dělá, raději se budou tvářit, že je to undefined. I když to klidně můžu zavolat v pure funkci. Se SIGSEGV to nemá nic společného.

andy

Re:Rychlost Haskell vs. C++
« Odpověď #124 kdy: 29. 08. 2018, 02:05:46 »
Jaká prasárna v pure kódu? Teď ti vůbec nerozumím. Error je něco jako přístup na nulovou adresu v C. Tam je taky zadefinované (na OS s MMU), že to vygeneruje SIGSEGV. Akorát ten error se dá potom v IO chytit. Proč bys proboha něco takového chtěl "normálně" používat? To je funkce, která vůbec není určená pro "normální" použití. Nebo snad normálně používáš přístup na nulovou adresu a chytání SIGSEGV jako "normální" ošetření chyb?
Srovnej s panic v Rustu. To je funkce, která se normálně používá a funguje naprosto definovaně. Volání error v Haskellu by mohlo taky, kdyby se chtělo. Akademikům se nechce, místo toho aby zadefinovali, co přesně volání error dělá, raději se budou tvářit, že je to undefined. I když to klidně můžu zavolat v pure funkci. Se SIGSEGV to nemá nic společného.
Nemohlo. V jazyce, který je lazy, nejde některé věci dělat stejně, jako ve striktním. A řešení chyb je přesně to, co musíš v lazy jazyku řešit kompletně jinak. Není totiž přesně definované, kdy vlastně dojde k vyhodnocení toho pure kódu (je to celé lazy!). Takže pokud použiješ "error" pro řešení chyb, tak se ti klidně může stát, že ta výjimka se vyhodí v úplně jiné části programu a klidně třeba i v jiném threadu - prostě tam, kde se zrovna pokusí někdo ta data přečíst. Prostě mimo jakýkoliv "catch", který jsi zrovna napsal.

Tohle je přesně jeden z důvodů, proč Haskell není jenom "další Java-like jazyk s trošku jinou syntaxí". Spousta věcí se řeší diametrálně odlišně a řešení z "normálních" jazyků jsou často zcela nepoužitelná. V pure kódu se prostě výjimky nevyhazují, a je velká snaha se zbavit i partial funkcí, kde to jen jde.

A vzhledem k tomu, že tento způsob řešení chyb je v Haskellu "zapovězen", tak samozřejmě existují jiné mechanismy. Které jsou mnohem lepší. Ono se to těžko popisuje, dokud to člověk nezkusí, ale práce s totálníma funkcema je naprosto jiný kafe.


lopata

Re:Rychlost Haskell vs. C++
« Odpověď #125 kdy: 29. 08. 2018, 02:28:49 »
Nemohlo. V jazyce, který je lazy, nejde některé věci dělat stejně, jako ve striktním.
Mohlo. Když už tam ten error je (lepší by bylo, kdyby vůbec nebyl), tak stačí zadefinovat, že volání error ukončí program a vypíše chybu. Ono se to stejně prakticky děje. Prakticky lidi totiž zajímá, jestli nějak rozumně zjistí, co se pokazilo.

lopata

Re:Rychlost Haskell vs. C++
« Odpověď #126 kdy: 29. 08. 2018, 02:33:20 »
Nemohlo. V jazyce, který je lazy, nejde některé věci dělat stejně, jako ve striktním.
Mohlo. Když už tam error je (to je další prasárna, lepší by bylo, kdyby vůbec nebyl), tak stačí zadefinovat, že volání error ukončí program a vypíše chybu. Ono se to stejně prakticky děje. Ani to nemusí být chytatelné v catch. Normální uživatele totiž zajímá, jestli nějak rozumně zjistí, co se pokazilo. Akademikům je to jedno, ti si vystačí s nekonečným cyklem.

andy

Re:Rychlost Haskell vs. C++
« Odpověď #127 kdy: 29. 08. 2018, 06:47:39 »
Nemohlo. V jazyce, který je lazy, nejde některé věci dělat stejně, jako ve striktním.
Mohlo. Když už tam error je (to je další prasárna, lepší by bylo, kdyby vůbec nebyl), tak stačí zadefinovat, že volání error ukončí program a vypíše chybu. Ono se to stejně prakticky děje. Ani to nemusí být chytatelné v catch. Normální uživatele totiž zajímá, jestli nějak rozumně zjistí, co se pokazilo. Akademikům je to jedno, ti si vystačí s nekonečným cyklem.
Takže by bylo lepší, kdyby tam byl jenom nekonečný cyklus, ale zároveň je to špatně, protože akademikové si vystačí s nekonečným cyklem a normální programátoři je nezajímají..? A úplně nejpraktičtější by bylo, kdyby podobná záležitost vůbec chytatelná nebyla a místo toho natvrdo ukončila program? Prostě to bude blbě, ať to udělají, jak to udělají...

Generovat výjimku v pure kódu a používat ji ke standardnímu ošetření chyb je prasárna. Bravo, právě jsi zjistil, že když budeš programovat jako prase, tak ti odstranění XStrict může "rozbít" už jednou rozbitý kód. Když budeš programovat jako prase v jiných jazycích, tak můžeš dopadnout úplně stejně. Ale prostě Haskellu to budeš do poslední síly vytýkat...

andy

Re:Rychlost Haskell vs. C++
« Odpověď #128 kdy: 29. 08. 2018, 07:07:34 »
Nemohlo. V jazyce, který je lazy, nejde některé věci dělat stejně, jako ve striktním.
Mohlo. Když už tam error je (to je další prasárna, lepší by bylo, kdyby vůbec nebyl), tak stačí zadefinovat, že volání error ukončí program a vypíše chybu. Ono se to stejně prakticky děje. Ani to nemusí být chytatelné v catch. Normální uživatele totiž zajímá, jestli nějak rozumně zjistí, co se pokazilo. Akademikům je to jedno, ti si vystačí s nekonečným cyklem.
Ještě poznámka k tomu tvému příkladu: on právě nedává sémanticky smysl. Na to, aby dal "správný" výstup, tak vevnitř závisíš na tom, že ti ta pure funkce "nevrátí výsledek". To, že v Haskellu právě díky praktičnosti jsi některé způsoby "nevrácení výsledku" schopen detekovat (v rámci možností dost deterministicky - dokonce i to zacyklení) nemění nic na tom, že ten tvůj program nedává smysl.

Jak píšu od začátku, je to na podobné úrovni, jako když bys v jiných jazycích závisel na nějakém side-efektu. Ono to dokonce může být i popsané a dost deterministické, ale program, jehož správné chování závisí třeba na tom, že se třeba někde vyhodí StackOverflow bys asi taky nepovažoval za dobrý příklad čehokoliv. A asi bys nepsal, že fakt, že StackOverflow je třeba exception a že jde v tom daném jazyce chytat je prasárna...

lopata

Re:Rychlost Haskell vs. C++
« Odpověď #129 kdy: 29. 08. 2018, 08:19:11 »
Nemohlo. V jazyce, který je lazy, nejde některé věci dělat stejně, jako ve striktním.
Mohlo. Když už tam error je (to je další prasárna, lepší by bylo, kdyby vůbec nebyl), tak stačí zadefinovat, že volání error ukončí program a vypíše chybu. Ono se to stejně prakticky děje. Ani to nemusí být chytatelné v catch. Normální uživatele totiž zajímá, jestli nějak rozumně zjistí, co se pokazilo. Akademikům je to jedno, ti si vystačí s nekonečným cyklem.
Takže by bylo lepší, kdyby tam byl jenom nekonečný cyklus, ale zároveň je to špatně, protože akademikové si vystačí s nekonečným cyklem a normální programátoři je nezajímají..? A úplně nejpraktičtější by bylo, kdyby podobná záležitost vůbec chytatelná nebyla a místo toho natvrdo ukončila program? Prostě to bude blbě, ať to udělají, jak to udělají...
Nepodsouvej mi něco, co jsem nenapsal. Bylo by lepší, kdyby se to chovalo DEFINOVANĚ. Prohlásit o error, který se vyskytuje v pure kódu i v Prelude na mnoha místech, že se chova nedefinovaně, je taková akademická Haskellovská prasárna. Normální lidi mají rádi deterministické programy, o kterých můžou říct, jak se budou chovat. Použitím error se z každého programu v Haskellu stává nedeterministický. Může to udělat v podstatě cokoliv. Když to dovedu ad absurdum, tak můžu říct, že bezpečnost Haskellu je někde na úrovni C, protože s použitím error taky nic nezaručuje a může se stát cokoliv.

Znovu opakuji, podívej se na panic v Rustu. Takhle se to má dělat správně, proti tomu řešení nemám žádné výhrady. Ono to jde, když se chce.

lopata

Re:Rychlost Haskell vs. C++
« Odpověď #130 kdy: 29. 08. 2018, 08:26:28 »
Ještě poznámka k tomu tvému příkladu: on právě nedává sémanticky smysl. Na to, aby dal "správný" výstup, tak vevnitř závisíš na tom, že ti ta pure funkce "nevrátí výsledek". To, že v Haskellu právě díky praktičnosti jsi některé způsoby "nevrácení výsledku" schopen detekovat (v rámci možností dost deterministicky - dokonce i to zacyklení) nemění nic na tom, že ten tvůj program nedává smysl.
Já nikde netvrdil, že můj program dává sémanticky smysl. Já tvrdil, že lazy versus strict vyhodnocení může změnit výsledek programu a to v Haskellu platí. Ty ses tady snažil tvrdit, že strict je jenom nějaká optimalizace, která mimo časových a paměťových záležitostí nemá sife effecty. Ale to prostě není pravda, v Haskellu to side effecty mít může a může to ovlivnit výsledek programu. A není potřeba řešit, který výstup programu je správně (má to při lazy dělení nulou spadnout, nebo ne?), prostě se to chová při lazy/strict jinak.

JSH

Re:Rychlost Haskell vs. C++
« Odpověď #131 kdy: 29. 08. 2018, 08:57:07 »
Nepodsouvej mi něco, co jsem nenapsal. Bylo by lepší, kdyby se to chovalo DEFINOVANĚ. Prohlásit o error, který se vyskytuje v pure kódu i v Prelude na mnoha místech, že se chova nedefinovaně, je taková akademická Haskellovská prasárna. Normální lidi mají rádi deterministické programy, o kterých můžou říct, jak se budou chovat. Použitím error se z každého programu v Haskellu stává nedeterministický. Může to udělat v podstatě cokoliv. Když to dovedu ad absurdum, tak můžu říct, že bezpečnost Haskellu je někde na úrovni C, protože s použitím error taky nic nezaručuje a může se stát cokoliv.

Znovu opakuji, podívej se na panic v Rustu. Takhle se to má dělat správně, proti tomu řešení nemám žádné výhrady. Ono to jde, když se chce.
Sorry, ale tohle je slovíčkaření. Error/panic/assert a všechno podobné znamená, že máš v programu chybu. V computer science je to všechno nedefinované chování, protože pád programu na hubu je čistě implementační záležitost. Je úplně jedno, jak přesně to specifikuješ. Ten error v Haskellu je podobně dobře specifikovaný, jako panic v rustu. Pokud se bavíme o výsledcích, tak ani ten panic v Rustu nemá žádný definovaný výsledek.

andy

Re:Rychlost Haskell vs. C++
« Odpověď #132 kdy: 29. 08. 2018, 09:02:27 »
Nepodsouvej mi něco, co jsem nenapsal. Bylo by lepší, kdyby se to chovalo DEFINOVANĚ. Prohlásit o error, který se vyskytuje v pure kódu i v Prelude na mnoha místech, že se chova nedefinovaně, je taková akademická Haskellovská prasárna. Normální lidi mají rádi deterministické programy, o kterých můžou říct, jak se budou chovat. Použitím error se z každého programu v Haskellu stává nedeterministický.
Principálně ano, speciálně, pokud třeba ta pure funkce je paralelizovaná, což klidně překladač může udělat. Může taky provádět spekulativní vyhodnocování apod. Pořadí vyhodnocování v lazy kódu je nedefinované... Ono je samozřejmě většinou vcelku jasné, jak se to vyhodnocuje, ale opravdu, pokud vyhodíš 2 výjimky v nezávislých větvích, které jsou obě potřebné pro vyhdnocení výsledku, tak která z toho vypadne je nedeterministické.

Citace
Může to udělat v podstatě cokoliv.
Fakt? Když je teda v typu, že to vrací Float, tak to vrátí Float nebo bottom. To je cokoliv? No když chceš...

Citace
Když to dovedu ad absurdum, tak můžu říct, že bezpečnost Haskellu je někde na úrovni C, protože s použitím error taky nic nezaručuje a může se stát cokoliv.
To by ovšem bylo poněkud absurdní, vzhledem k tomu, že haskell ti přesně zaručuje, že to pure funkce, kterou voláš, se bude chovat přesně podle toho typu (tzn. vrátí návratovou hodnotu nebo bude bottom). Což se přesně děje...

Citace
Znovu opakuji, podívej se na panic v Rustu. Takhle se to má dělat správně, proti tomu řešení nemám žádné výhrady. Ono to jde, když se chce.
No jestli v tebou napsaném kódu běžně používáš panic pro řešení běžných stavů, tak to potěš...

Ty se strašně divíš, že konstrukce, které jsou vcelku použitelné ve striktních jazycích prostě v lazy jazycích nedávají moc smysl. Ano, nedávají. Proto je taky Haskell považován za tak "těžký", protože i ten blbý error handling musíš dělat jinak.

lopata

Re:Rychlost Haskell vs. C++
« Odpověď #133 kdy: 29. 08. 2018, 09:20:49 »
Sorry, ale tohle je slovíčkaření. Error/panic/assert a všechno podobné znamená, že máš v programu chybu. V computer science je to všechno nedefinované chování, protože pád programu na hubu je čistě implementační záležitost.
Tobě uniká základní věc, rozdíl mezi undefined behavior a definovaným ukončením programu (ať už standardně nebo chybou). Panic v Rustu NENÍ undefined behaviour. Používá se naprosto standardně, má definované chování a řeší situace, kdy nastane unrecoverable error. Zkus si trochu rozšířit obzory a nastudovat třeba tohle: https://doc.rust-lang.org/book/second-edition/ch09-03-to-panic-or-not-to-panic.html. Ukončení programu (ať už panicem nebo errorem) nemá být implementační záležitost. Haskell to má blbě, protože v Haskellu to implementační záležitost je a dokonce se ten program ani ukončit nemusí a zůstane v nekonečném cyklu... Tohle je prostě špatný design a s computer science to má společného možná jenom to, že computer science nedefinuje, co přesně se má v programu stát, když nastane unrecoverable error, takže Haskell akademici (včetně tebe) si to vyložili tak, že je to vlasně úplně jedno a nemusí to řešit. Normálním lidem to ale jedno není a řešit to chtějí.

Je úplně jedno, jak přesně to specifikuješ. Ten error v Haskellu je podobně dobře specifikovaný, jako panic v rustu. Pokud se bavíme o výsledcích, tak ani ten panic v Rustu nemá žádný definovaný výsledek.
Panic v Rustu má definovaný výsledek. Program se ukončí na na výstup se vypíše chyba. Chyba na výstupu je definovaný výsledek.

andy

Re:Rychlost Haskell vs. C++
« Odpověď #134 kdy: 29. 08. 2018, 09:29:12 »
Ukončení programu (ať už panicem nebo errorem) nemá být implementační záležitost. Haskell to má blbě, protože v Haskellu to implementační záležitost je a dokonce se ten program ani ukončit nemusí a zůstane v nekonečném cyklu...
Ale ono je to popsané IMO i v haskell reportu, že se to chová tak, jak se to chová. O to vůbec nejde - takhle dopadaj zkratky v diskuzích...

Jde o to, že se snažíš příklad rozbitého programu (v normálním flow má error) prezentovat jako "nerozbitý", který následně odstranění XStrict rozbije. Tak se ti snažím nějak vysvětlit, že pokud má v normálním flow "error", tak už to je rozbité.

Chápu, že ty "nechceš", aby měl error takovouhle sémantiku, a protože ty to "nechceš" a tobě se "líbí", když s tím můžeš normálně pracovat, protože naprosto běžně používáš panic pro řešení chybových stavů. Tobě se "líbí", když můžeš z pure funkce vyhodit výjimku a pak s ní normálně pracovat a to, že v Haskellu tímhle způsobem nejde udělat (resp. se to nepovažuje za korektní), je prostě "špatně".