Funkcionální programátor

Greenhorn

Re:Funkcionální programátor
« Odpověď #330 kdy: 07. 07. 2015, 22:20:07 »
Když můžu v Haskellu napsat kód, který vypadá a chová se úplně stejně jako kód napsaný v Cčku, tak Haskell prostě není pure.
No, to je dost zvláštní tvrzení. Představ si, že bys měl main funkci, která by vracela "seznam IO akcí" - načti tohle, vraž to sem, zapiš tohle tam. Výsledkem main funkce by byl jenom tenhle seznam. což je statická abstraktní struktura, kterou jistě můžeš generovat čistě.

...no a tuhle strukturu prostě vezmeš a v runtimu uděláš ty akce. Co je na tom teda nečistého a podle jaké definice?

No a tohle je v podstatě přesně to, co IO monáda dělá.
Když jsem se koukal na vnitřní implementaci IO monády, tak mi to přišlo jako prostá kompozice lambd. A to se od lazy streams dost liší. V případě lazy streams člověk do runtimu nezasahuje, nemá do něj přístup, prostě jen řekne co chce. V případě IO monády člověk to podhoubí pracující se světem sám píše. Neříká co chce, jen přes wrapper přímo volá Cčkovské funkce. Opět zdůrazňuji, jsem jen uživatel Haskellu se selským rozumem, nepochybuji že pomocí vhodných argumentů by se můj dojem dal roztrhat na cucky. Ale pro mne by to nic nezměnilo, jakožto uživatel se budu potýkat se stejnými problémy jako doteď.


v

Re:Funkcionální programátor
« Odpověď #331 kdy: 07. 07. 2015, 22:23:32 »
vypíše (0,1), tj. get vrátí pokaždé jinou hodnotu, přesto je f čistá funkce, čarodějnictví?!
Ovšem to "a <- get" není, pokud se nepletu, normální volání normální funkce. Takže to "různá volání stejné funkce vrátí jinou hodnotu" je přinejmenším zavádějící.

Teď si z hlavy nevybavím, jak to tam přesně je, ale v principu to bude syntaktický cukr pro dvoje volání nějaké lambdy s různými parametry.

když vyjdu z čumilovy definice monády tak to jsou dvě volání stejné funkce se stejnými argumenty v pozměněném prostředí

Re:Funkcionální programátor
« Odpověď #332 kdy: 07. 07. 2015, 22:25:12 »
Neříká co chce, jen přes wrapper přímo volá Cčkovské funkce.
No a to právě AFAIK není pravda. Z té funkce, která dělá IO (např. main) prostě vypadnou ty lambdy. Ty v kódu NEvoláš céčkovské funkce, ale vytváříš lambdy, které předáš runtimu.

(pokud se mýlím, doufám, že mě zkušenější kolegové poopraví)

Ale pro mne by to nic nezměnilo, jakožto uživatel se budu potýkat se stejnými problémy jako doteď.
Monády dělají jenom iluzi imperativního kódu. Všechno je to jenom syntaktický cukr. Třeba "return" není žádný return. Kdyby sis místo do blocku to celé rozepsal do těch lambd, tak by tě to třeba tak nedráždilo a tu čistotu byz nezpochybňoval, protože bys ji tam měl jak na talíři :)

Re:Funkcionální programátor
« Odpověď #333 kdy: 07. 07. 2015, 22:27:02 »
když vyjdu z čumilovy definice monády tak to jsou dvě volání stejné funkce se stejnými argumenty v pozměněném prostředí
Tady o žádnou čumilovu definici nejde. Význam symbolu "<-" je jasně definovaný, čumil nečumil ;) A dvoje volání téže funkce se stejnými parametry to prostě není.

Greenhorn

Re:Funkcionální programátor
« Odpověď #334 kdy: 07. 07. 2015, 22:27:22 »
vstup/výstup v čistě a čistěji funkcionálních jazycích je složitý problém, jaké překvapení
čumil nic nemohl uvést na pravou mírou bo tomu sám nerozumí, viz jeho dlouhý post a to co psal o měnitelných referencích v IO monádě
A co o nich psal špatného? Mne se jeho logika příjemně chápala, byla jasná a stručná. Prosím, neříkejte o někom že něčemu vůbec nerozumí, já bych si něco takového už jen z pohledu vlastních zkušeností nedovolil. A vy máte zkušenosti podobné jako já, pokud vycházím z vytvořených projektů.

uvedl měnitelné reference jako důkaz nečistototy io monády, ale ty se dají implementovat i ve State monádě, která je 100% čistá, krom toho je to v rozporu s definicí kterou sám uvedl - funkce v prostředí, zápis do reference způsobí změnu prostředí
Zajímavé. Mohl by jste to první více vysvětlit? A druhou část komentáře jsem bohužel nepochopil.

ve State monádě lze implementovat měnitelné reference, funkce jako createVar, readVar, writeVar, uvedel jsem tayd kus kódy, který demonstruje kdy v čistém kódu dvě různá volání stejné funkce vrátí jinou hodnotu
O tom nic nevím. Byl by jste tak hodný, a napsal delší okomentovanou ukázku?

import Control.Monad.State

f = evalState g 0
   where g = do
      a <- get
      put 1
      b <- get
      return (a, b)

main = print f

vypíše (0,1), tj. get vrátí pokaždé jinou hodnotu, přesto je f čistá funkce, čarodějnictví?!
Koukal jsem se na to, a nikde nevidím měnitelný stav. Když si to rozepíšeme pomocí klasických monadických operátorů, jasně uvidíme že měnitelný stav tam není, jen se vytvářejí nové kopie State monády, která v sobě má jiný stav, takže funkce get pokaždé vrátí něco jiného. Je zavolána s jinou instancí State monády.

Kód: [Vybrat]
f = evalState g 0
   where g =
      get >>= (\a ->
      put 1 >>
      get >>= (\b ->
      return (a, b)))

main = print f


Greenhorn

Re:Funkcionální programátor
« Odpověď #335 kdy: 07. 07. 2015, 22:32:17 »
vypíše (0,1), tj. get vrátí pokaždé jinou hodnotu, přesto je f čistá funkce, čarodějnictví?!
Ovšem to "a <- get" není, pokud se nepletu, normální volání normální funkce. Takže to "různá volání stejné funkce vrátí jinou hodnotu" je přinejmenším zavádějící.

Teď si z hlavy nevybavím, jak to tam přesně je, ale v principu to bude syntaktický cukr pro dvoje volání nějaké lambdy s různými parametry.
když vyjdu z čumilovy definice monády tak to jsou dvě volání stejné funkce se stejnými argumenty v pozměněném prostředí
Monády slouží pro zřetězení operací v nějakém kontextu, vždyť se to o nich i říká v oficiální dokumentaci. To nevymyslel čumil. Ale vnitřně monády nemají měnitelný stav. A ani navenek, při každé změně nová kopie. I ty měnitelný reference jsou neměnný, ale to na co ukazují již ano.

Greenhorn

Re:Funkcionální programátor
« Odpověď #336 kdy: 07. 07. 2015, 22:38:29 »
Neříká co chce, jen přes wrapper přímo volá Cčkovské funkce.
No a to právě AFAIK není pravda. Z té funkce, která dělá IO (např. main) prostě vypadnou ty lambdy. Ty v kódu NEvoláš céčkovské funkce, ale vytváříš lambdy, které předáš runtimu.

(pokud se mýlím, doufám, že mě zkušenější kolegové poopraví)

Ale pro mne by to nic nezměnilo, jakožto uživatel se budu potýkat se stejnými problémy jako doteď.
Monády dělají jenom iluzi imperativního kódu. Všechno je to jenom syntaktický cukr. Třeba "return" není žádný return. Kdyby sis místo do blocku to celé rozepsal do těch lambd, tak by tě to třeba tak nedráždilo a tu čistotu byz nezpochybňoval, protože bys ji tam měl jak na talíři :)
Vytvořit lambdu která volá Cčkovskou funkci není čisté, a rozhodně o nic víc než zavolání té funkce rovnou. A runtimu se nic nepředává, runtime Haskellu nemá žádný modul pro IO. A i kdyby se předávalo, na faktu že v čistém jazyce píšeme kód s vedlejšími efekty se nic nezmění.

v

Re:Funkcionální programátor
« Odpověď #337 kdy: 07. 07. 2015, 22:41:22 »
vstup/výstup v čistě a čistěji funkcionálních jazycích je složitý problém, jaké překvapení
čumil nic nemohl uvést na pravou mírou bo tomu sám nerozumí, viz jeho dlouhý post a to co psal o měnitelných referencích v IO monádě
A co o nich psal špatného? Mne se jeho logika příjemně chápala, byla jasná a stručná. Prosím, neříkejte o někom že něčemu vůbec nerozumí, já bych si něco takového už jen z pohledu vlastních zkušeností nedovolil. A vy máte zkušenosti podobné jako já, pokud vycházím z vytvořených projektů.

uvedl měnitelné reference jako důkaz nečistototy io monády, ale ty se dají implementovat i ve State monádě, která je 100% čistá, krom toho je to v rozporu s definicí kterou sám uvedl - funkce v prostředí, zápis do reference způsobí změnu prostředí
Zajímavé. Mohl by jste to první více vysvětlit? A druhou část komentáře jsem bohužel nepochopil.

ve State monádě lze implementovat měnitelné reference, funkce jako createVar, readVar, writeVar, uvedel jsem tayd kus kódy, který demonstruje kdy v čistém kódu dvě různá volání stejné funkce vrátí jinou hodnotu
O tom nic nevím. Byl by jste tak hodný, a napsal delší okomentovanou ukázku?

import Control.Monad.State

f = evalState g 0
   where g = do
      a <- get
      put 1
      b <- get
      return (a, b)

main = print f

vypíše (0,1), tj. get vrátí pokaždé jinou hodnotu, přesto je f čistá funkce, čarodějnictví?!
Koukal jsem se na to, a nikde nevidím měnitelný stav. Když si to rozepíšeme pomocí klasických monadických operátorů, jasně uvidíme že měnitelný stav tam není, jen se vytvářejí nové kopie State monády, která v sobě má jiný stav, takže funkce get pokaždé vrátí něco jiného. Je zavolána s jinou instancí State monády.

Kód: [Vybrat]
f = evalState g 0
   where g =
      get >>= (\a ->
      put 1 >>
      get >>= (\b ->
      return (a, b)))

main = print f

jak víte, že tam není měnitelný stav? jak víte, že u ioref v io monádě je? co když na tom nezáleží?

v

Re:Funkcionální programátor
« Odpověď #338 kdy: 07. 07. 2015, 22:41:57 »
a co kdybych v příkladu použil ST monádu?

Greenhorn

Re:Funkcionální programátor
« Odpověď #339 kdy: 07. 07. 2015, 22:45:00 »
Neříká co chce, jen přes wrapper přímo volá Cčkovské funkce.
No a to právě AFAIK není pravda. Z té funkce, která dělá IO (např. main) prostě vypadnou ty lambdy. Ty v kódu NEvoláš céčkovské funkce, ale vytváříš lambdy, které předáš runtimu.

(pokud se mýlím, doufám, že mě zkušenější kolegové poopraví)

Ale pro mne by to nic nezměnilo, jakožto uživatel se budu potýkat se stejnými problémy jako doteď.
Monády dělají jenom iluzi imperativního kódu. Všechno je to jenom syntaktický cukr. Třeba "return" není žádný return. Kdyby sis místo do blocku to celé rozepsal do těch lambd, tak by tě to třeba tak nedráždilo a tu čistotu byz nezpochybňoval, protože bys ji tam měl jak na talíři :)
Čistotu IO monády jsem začal zpochybňovat od té doby, co jsem ji začal používat i k něčemu složitějšímu než čtení a zapisování do konzole. Ačkoli, já jsem ji nezačal zpochybňovat, já jsem se snažil zoufale pochopit jak to může být funkcionální. Řešení je prosté, není. A rozepsání nic nemění. Po rozepsání to sice nebude vypadat jak kód v Cčku, ale chovat se bude stejně.

Greenhorn

Re:Funkcionální programátor
« Odpověď #340 kdy: 07. 07. 2015, 22:50:14 »
vstup/výstup v čistě a čistěji funkcionálních jazycích je složitý problém, jaké překvapení
čumil nic nemohl uvést na pravou mírou bo tomu sám nerozumí, viz jeho dlouhý post a to co psal o měnitelných referencích v IO monádě
A co o nich psal špatného? Mne se jeho logika příjemně chápala, byla jasná a stručná. Prosím, neříkejte o někom že něčemu vůbec nerozumí, já bych si něco takového už jen z pohledu vlastních zkušeností nedovolil. A vy máte zkušenosti podobné jako já, pokud vycházím z vytvořených projektů.

uvedl měnitelné reference jako důkaz nečistototy io monády, ale ty se dají implementovat i ve State monádě, která je 100% čistá, krom toho je to v rozporu s definicí kterou sám uvedl - funkce v prostředí, zápis do reference způsobí změnu prostředí
Zajímavé. Mohl by jste to první více vysvětlit? A druhou část komentáře jsem bohužel nepochopil.

ve State monádě lze implementovat měnitelné reference, funkce jako createVar, readVar, writeVar, uvedel jsem tayd kus kódy, který demonstruje kdy v čistém kódu dvě různá volání stejné funkce vrátí jinou hodnotu
O tom nic nevím. Byl by jste tak hodný, a napsal delší okomentovanou ukázku?

import Control.Monad.State

f = evalState g 0
   where g = do
      a <- get
      put 1
      b <- get
      return (a, b)

main = print f

vypíše (0,1), tj. get vrátí pokaždé jinou hodnotu, přesto je f čistá funkce, čarodějnictví?!
Koukal jsem se na to, a nikde nevidím měnitelný stav. Když si to rozepíšeme pomocí klasických monadických operátorů, jasně uvidíme že měnitelný stav tam není, jen se vytvářejí nové kopie State monády, která v sobě má jiný stav, takže funkce get pokaždé vrátí něco jiného. Je zavolána s jinou instancí State monády.

Kód: [Vybrat]
f = evalState g 0
   where g =
      get >>= (\a ->
      put 1 >>
      get >>= (\b ->
      return (a, b)))

main = print f

jak víte, že tam není měnitelný stav? jak víte, že u ioref v io monádě je? co když na tom nezáleží?
U IORef to vím jistě protože jsem ji dost používal, tam prostě není možné dělat nové kopie IORef, člověk ji používá aby se ty kopie právě nedělali, jinak by komunikace mezi handlery nefungovala. Zkuste si napsat GUI aplikaci, budete ji muset použít taky, a měnitelný stav na vás bude křičet. A nakonec, záleží na tom, to je také možné pak říci, vždyť na čistotě nezáleží, pojďme na C++.

Greenhorn

Re:Funkcionální programátor
« Odpověď #341 kdy: 07. 07. 2015, 22:54:08 »
a co kdybych v příkladu použil ST monádu?
Z toho co jsem si přečetl, tak ST monáda je kapsa nečistoty v čistém kódu. Může být v čistém kódu, protože pomocí ST monády není možné dělat vedlejší efekty, jenom v té kapse můžeme mít měnitelný stav pro efektivní zalgoritmizovaní nějakého problému. Z vnějšku je kapsa čistá, ve vnitřku nikoli.

Re:Funkcionální programátor
« Odpověď #342 kdy: 07. 07. 2015, 22:56:50 »
Vytvořit lambdu která volá Cčkovskou funkci není čisté, a rozhodně o nic víc než zavolání té funkce rovnou. A runtimu se nic nepředává, runtime Haskellu nemá žádný modul pro IO. A i kdyby se předávalo, na faktu že v čistém jazyce píšeme kód s vedlejšími efekty se nic nezmění.
Ještě jednou: ve funkci main jenom vytvoříš datovou strukturu, která popisuje nějaký sled akcí. Pokud generování datové struktury "není čisté", tak už fakt nevím, co by mělo být čisté...

Čistotu IO monády jsem začal zpochybňovat od té doby, co jsem ji začal používat i k něčemu složitějšímu než čtení a zapisování do konzole. Ačkoli, já jsem ji nezačal zpochybňovat, já jsem se snažil zoufale pochopit jak to může být funkcionální. Řešení je prosté, není. A rozepsání nic nemění. Po rozepsání to sice nebude vypadat jak kód v Cčku, ale chovat se bude stejně.
Ale jistěže to je funkcionální, v tom nejstriktnějším možném slova smyslu. Pokud tomu nevěříš, tak si to asi budeš muset nastudovat do hloubky včetně teorie kategorií, kde to uvidíš jasně a důkaz tam budeš mít rigorozní.

Pokud máš pocit, že "to funkcionální není", tak ty důvody rozepiš formálně: podej definici "funkcionální" a ukaž, který z bodů to podle tebe nesplňuje. Skoro bych řekl, že by ti mělo být předem jasné, že nemáš šanci uspět, protože za touhle celou strukturou jsou mozky, které fakt nedělají nějaké hloupé chyby...

Greenhorn

Re:Funkcionální programátor
« Odpověď #343 kdy: 07. 07. 2015, 23:12:20 »
Vytvořit lambdu která volá Cčkovskou funkci není čisté, a rozhodně o nic víc než zavolání té funkce rovnou. A runtimu se nic nepředává, runtime Haskellu nemá žádný modul pro IO. A i kdyby se předávalo, na faktu že v čistém jazyce píšeme kód s vedlejšími efekty se nic nezmění.
Ještě jednou: ve funkci main jenom vytvoříš datovou strukturu, která popisuje nějaký sled akcí. Pokud generování datové struktury "není čisté", tak už fakt nevím, co by mělo být čisté...

Čistotu IO monády jsem začal zpochybňovat od té doby, co jsem ji začal používat i k něčemu složitějšímu než čtení a zapisování do konzole. Ačkoli, já jsem ji nezačal zpochybňovat, já jsem se snažil zoufale pochopit jak to může být funkcionální. Řešení je prosté, není. A rozepsání nic nemění. Po rozepsání to sice nebude vypadat jak kód v Cčku, ale chovat se bude stejně.
Ale jistěže to je funkcionální, v tom nejstriktnějším možném slova smyslu. Pokud tomu nevěříš, tak si to asi budeš muset nastudovat do hloubky včetně teorie kategorií, kde to uvidíš jasně a důkaz tam budeš mít rigorozní.

Pokud máš pocit, že "to funkcionální není", tak ty důvody rozepiš formálně: podej definici "funkcionální" a ukaž, který z bodů to podle tebe nesplňuje. Skoro bych řekl, že by ti mělo být předem jasné, že nemáš šanci uspět, protože za touhle celou strukturou jsou mozky, které fakt nedělají nějaké hloupé chyby...
Ano, vytvoří se neměnná datová struktura, v které jsou lambdy s vedlejšími efekty. A když se tato struktura poté vyhodnocuje, začnou se vedlejší efekty projevovat. Mám dvě otázky. Pokud je IO monáda čistá, lze udělat následující strukturu kódu

-----------------------------------------------
IO
-----------------------------------------------
Bez IO monády
-----------------------------------------------
IO
-----------------------------------------------

ale v Haskellu to nejde. Neexisuje něco jako runIO protože runIO nelze nahradit svým výstupem. A druhá otázka, pokud je IO monáda čistá, proč ji nemůžeme zparalelizovat pomocí par? Neměl by to být problém ne? Ale v Haskellu to nejde. Par ignoruje IO monádu.

Třeba u STRef je možnost zavolat runST, a tím umístit ST monádu i do čistého kódu, ST monáda je ale navenek čistá, neumožňuje vedlejší efekty, takže výstup je deterministický ačkoli ve vnitřku ST monáda čistá není.

Re:Funkcionální programátor
« Odpověď #344 kdy: 07. 07. 2015, 23:23:53 »
Pokud je IO monáda čistá, lze udělat následující strukturu kódu
Sorry, ale tomu zápisu nerozumím, nevím, co jsou ty čáry atd. Nešlo by ukázat normální kód, který se v Haskellu nepřeloží?

Neexisuje něco jako runIO protože runIO nelze nahradit svým výstupem.
Tady se už dostávám na tenkej led, takže opět doufám, že to někdo případně opraví: nejde to právě proto, že ty potřebuješ IO vzít a vrátit ho runtimu jako návrat funkce main. V jazyku samotném prostě tu datovou strukturu popisující IO nemůžeš spustit. To je přesně ta moje pointa.

Můžeš tu monádu předávat celým program a vyplivnout ji do main, ale nemůžeš se jí uprostřed programu "zbavit" = "spustit ji" - a to právě proto, že kdyby to bylo možné, pak by Haskell čistý nebyl. Možné to není a Haskell čistý je.

A druhá otázka, pokud je IO monáda čistá, proč ji nemůžeme zparalelizovat pomocí par?  Neměl by to být problém ne? Ale v Haskellu to nejde. Par ignoruje IO monádu.
Tohle už je na mě moc implementačně-specifická otázka, do té se raději pouštět nebudu, nejsem praktický haskeller.