it-swarm-eu.dev

Proč jsou vedlejší účinky považovány za funkční ve funkčním programování?

Cítím, že vedlejší účinky jsou přirozený jev. Ale je to něco jako tabu ve funkčních jazycích. Jaké jsou důvody?

Moje otázka se týká funkčního programovacího stylu. Ne všechny programovací jazyky/paradigmy.

70
Gulshan

Psaní vašich funkcí/metod bez vedlejších účinků - takže jsou čistými funkcemi - usnadňují zdůvodnění správnosti programu.

Také usnadňuje vytváření těchto funkcí a vytváření nového chování.

Umožňuje také určité optimalizace, kdy kompilátor může například zaznamenávat výsledky funkcí, nebo použít Common Subexpression Elimination.

Úpravy: na žádost Benjola: Protože mnoho vašeho státu je uloženo v zásobníku (datový tok, nikoli kontrolní tok, jak to nazval Jonas zde ), můžete paralelizovat nebo jinak změnit pořadí provádění těchto částí vašeho výpočtu, které jsou na sobě nezávislé. Tyto nezávislé části můžete snadno najít, protože jedna část neposkytuje vstupy druhé.

V prostředích s debuggery, které vám umožní vrátit se zpět do zásobníku a pokračovat v práci na počítači (jako Smalltalk), mít čisté funkce znamená, že můžete velmi snadno vidět, jak se hodnota mění, protože předchozí stavy jsou k dispozici pro kontrolu. Ve výpočtu těžkých mutací, pokud do své struktury nebo algoritmu výslovně nepřidáte akce do/zpět, nemůžete vidět historii výpočtu. (To navazuje na první odstavec: psaní čistých funkcí usnadňuje kontrolu správnosti vašeho programu.)

73
Frank Shearar

Máte špatně, funkční programování omezuje vedlejší účinky, aby programy byly snadno pochopitelné a optimalizovatelné. Dokonce i Haskell vám umožňuje zapisovat do souborů.

V podstatě říkám, že funkční programátoři si nemyslí, že vedlejší účinky jsou zlí, prostě si myslí, že omezení použití vedlejších účinků je dobré. Vím, že to může vypadat jako takové jednoduché rozlišení, ale to dělá rozdíl.

24
ChaosPandion

Z článku o funkční programování :

V praxi musí mít aplikace některé vedlejší účinky. Simon Peyton-Jones, hlavní přispěvatel do funkčního programovacího jazyka Haskell, uvedl: „Nakonec musí každý program manipulovat státem. Program, který nemá žádné vedlejší účinky, je druh černé skříňky. Vše, co můžete říct, je že box se zahřeje. “ ( http://oscon.blip.tv/file/324976 ) Klíčem je omezit vedlejší účinky, jasně je identifikovat a zabránit jejich rozptýlení po celém kódu.

23
Peter Stuifzand

Několik poznámek:

  • Funkce bez vedlejších účinků lze trivialy provádět paralelně, zatímco funkce s vedlejšími efekty obvykle vyžadují určitý druh synchronizace.

  • Funkce bez vedlejších účinků umožňují agresivnější optimalizaci (např. Transparentním použitím mezipaměti výsledků), protože pokud získáme správný výsledek, nezáleží ani na tom, zda byla funkce opravd popraven

13
user281377

Nyní pracuji hlavně ve funkčním kódu a z tohoto pohledu se zdá oslepující. Vedlejší účinky vytvářejí obrovské mentální zatížení programátorů, kteří se snaží číst a porozumět kódu. Nevšimnete si toho břemene, dokud z něj nebudete na chvilku osvobozeni, a pak najednou musíte znovu přečíst kód s vedlejšími účinky.

Zvažte tento jednoduchý příklad:

val foo = 42
// Several lines of code you don't really care about, but that contain a
// lot of function calls that use foo and may or may not change its value
// by side effect.

// Code you are troubleshooting
// What's the expected value of foo here?

Ve funkčním jazyce vím , že foo je stále 42. Nemusím ani podívejte se na kód mezi tím, mnohem méně tomu rozumíte, nebo se podívejte na implementaci funkcí, které volá.

Vše o souběžnosti a paralelizaci a optimalizaci je pěkné, ale to je to, co počítačoví vědci vložili do brožury. Nemusím se divit, kdo mutuje vaši proměnnou a kdy je to, co mě v každodenní praxi opravdu baví.

11
Karl Bielefeldt

Jen málo jazyků znemožňuje vedlejší účinky. Používání jazyků, které byly zcela bez vedlejších účinků, by bylo obtížné (téměř nemožné) používat, s výjimkou velmi omezené kapacity.

Proč jsou vedlejší účinky považovány za zlé?

Protože je mnohem obtížnější uvažovat o tom, co přesně program dělá, a dokázat, že dělá to, co od něj očekáváte.

Na velmi vysoké úrovni si představte testování celého 3vrstvého webu s pouze testováním blackboxu. Jistě, je to proveditelné, v závislosti na měřítku. Ale určitě se děje hodně duplikace. A pokud existuje is chyba (která souvisí s vedlejším účinkem), pak byste mohli potenciálně rozbít celý systém pro další testování, dokud nebude chyba diagnostikována a opravena a oprava není nasazeno do testovacího prostředí.

Výhody

Nyní to zmenšete. Pokud byste byli docela dobře v psaní kódu vedlejších účinků, o kolik rychleji byste uvažovali o tom, co nějaký existující kód udělal? O kolik rychleji byste mohli psát testy jednotek? Jak jste si jisti, že kód bez vedlejších účinků byl zaručen bez chyb a že uživatelé mohli omezit svou expozici na jakékoli chyby, které má udělal?

Pokud kód nemá žádné vedlejší účinky, kompilátor může mít také další optimalizace, které by mohl provést. Implementace těchto optimalizací může být mnohem snazší. Může být mnohem snazší dokonce navrhnout optimalizaci pro kód bez vedlejších účinků, což znamená, že dodavatel kompilátoru může implementovat optimalizace, které jsou v kódu s vedlejšími účinky obtížně nemožné.

Souběžnost je také výrazně jednodušší implementovat, automaticky generovat a optimalizovat, když kód nemá žádné vedlejší účinky. Je to proto, že všechny kusy lze bezpečně vyhodnotit v libovolném pořadí. Umožnění programátorům psát vysoce souběžný kód je široce považováno za další velkou výzvu, kterou musí informatika řešit, a za jedno z mála zbývajících zajištění proti Mooreův zákon .

6

Vedlejší účinky jsou jako „netěsnosti“ ve vašem kódu, které budete muset vyřešit později, ať už vy, nebo nějaký netušící spolupracovník.

Funkční jazyky se vyhýbají stavovým proměnným a proměnlivým datům, aby způsobily, že kód bude méně závislý na kontextu a bude modulárnější. Modularita zajišťuje, že práce jednoho vývojáře neovlivní/neoslabí práci jiného.

Měřítko rychlosti vývoje s velikostí týmu je dnes „svatým grálem“ vývoje softwaru. Při práci s jinými programátory je několik důležitých věcí jako modularita. Dokonce i ty nejjednodušší logické vedlejší účinky způsobují, že spolupráce je velmi obtížná.

4
Ami

No, IMHO, to je docela pokrytecké. Nikdo nemá rád vedlejší účinky, ale každý je potřebuje.

Co je tak nebezpečného na vedlejších účincích, je to, že pokud zavoláte nějakou funkci, pak to pravděpodobně ovlivní nejen způsob, jakým se funkce chová při příštím vyvolání, ale pravděpodobně to má vliv i na další funkce. Vedlejší účinky tedy zavádějí nepředvídatelné chování a netriviální závislosti.

Programovací paradigmata jako OO a funkční jak tento problém řeší. OO zmenšuje problém ukládáním oddělení problémů. To znamená stav aplikace, který se skládá z Mnoho proměnných dat je zapouzdřeno do objektů, z nichž každý je zodpovědný pouze za udržování svého vlastního stavu. Tím se snižuje riziko závislostí a problémy jsou mnohem izolovanější a snáze sledovatelné.

Funkční programování vyžaduje mnohem radikálnější přístup, kde aplikační stav je jednoduše neměnný z pohledu programátora. To je pěkný nápad, ale činí tento jazyk zbytečným sám o sobě. Proč? Protože ŽÁDNÁ I/O-operace má vedlejší účinky. Jakmile budete číst z jakéhokoli vstupního proudu, stav aplikace se pravděpodobně změní, protože při příštím vyvolání stejné funkce bude výsledek pravděpodobně jiný. Možná čtete různá data nebo - možná také - operace může selhat. Totéž platí pro výstup. Rovnoměrný výstup je operace s vedlejšími účinky. To není nic, čeho si v dnešní době často uvědomujete, ale představte si, že máte na výstup pouze 20 kB a pokud výstup ještě více ztratí aplikace, protože vám zbývá místo na disku nebo cokoli jiného.

Takže ano, nežádoucí účinky jsou ošklivé a nebezpečné z pohledu programátora. Většina chyb pochází ze způsobu, jakým jsou některé části stavu aplikace blokovány téměř temným způsobem, skrze nezohledněné a často zbytečné vedlejší účinky. Z pohledu uživatele jsou vedlejšími účinky místo používání počítače. Nestarají se o to, co se děje uvnitř nebo jak je to organizováno. Dělají něco a podle toho očekávají, že se počítač změní.

4
back2dos

Jakýkoli vedlejší účinek zavádí další vstupní/výstupní parametry, které je třeba vzít v úvahu při testování.

Toto dělá ověření kódu mnohem složitějším, protože prostředí nemůže být omezeno pouze na ověřovaný kód, ale musí přinést některé nebo všechny okolní prostředí (globální, který je aktualizován, žije v tomto kódu tam, což zase závisí na tom, že kód, který zase závisí na žití uvnitř plného Java EE server ....)

Snažíte se vyhnout vedlejším účinkům, omezujete množství externalismu potřebného ke spuštění kódu.

2
user1249

Podle mých zkušeností dobrý design v objektově orientovaném programování vyžaduje použití funkcí, které mají vedlejší účinky.

Vezměte například základní desktopovou aplikaci uživatelského rozhraní. Možná mám spuštěný program, který má na své haldě objektový graf představující aktuální stav modelu domény mého programu. Zprávy přicházejí k objektům v tomto grafu (například prostřednictvím volání metod vyvolaných z řadiče vrstvy uživatelského rozhraní). Graf objektu (model domény) na haldě je upraven v reakci na zprávy. Pozorovatelé modelu jsou informováni o všech změnách, uživatelské rozhraní a možná i další zdroje jsou upraveny.

Korektní uspořádání těchto vedlejších účinků modifikujících haldu a obrazovku modifikace není ani zlé, jádrem návrhu OO (v tomto případě vzoru MVC)).

To samozřejmě neznamená, že by vaše metody měly arbitrární vedlejší účinky. A funkce bez vedlejších účinků mají místo ke zlepšení čitelnosti a někdy i výkonu vašeho kódu.

1
flamingpenguin

Jak již bylo uvedeno výše, funkční jazyky tolik nebrání kódu v tom, aby měly vedlejší účinky, protože nám poskytují nástroje pro správu toho, jaké vedlejší účinky mohou stane se v daném kusu kódu a kdy.

Ukázalo se, že to má velmi zajímavé důsledky. Za prvé a nejzřejmější je, že s kódem vedlejších účinků, které již byly popsány, můžete dělat řadu věcí. Ale existují i ​​další věci, které můžeme udělat, i když pracujeme s kódem, který má vedlejší účinky:

  • V kódu s proměnlivým stavem můžeme spravovat rozsah státu takovým způsobem, který staticky zajistí, že nemůže uniknout mimo danou funkci, což nám umožňuje shromažďovat odpadky bez referenčních schémat počítání nebo schémat stylu značení a zametání , přesto se ujistěte, že žádné odkazy přežijí. Stejné záruky jsou také užitečné pro zachování informací citlivých na soukromí atd. (Toho lze dosáhnout použitím ST monad v haskell)
  • Při úpravě sdíleného stavu ve více vláknech se můžeme vyhnout potřebám zámků sledováním změn a prováděním atomové aktualizace na konci transakce nebo vrácením transakce zpět a opakováním, pokud jiné vlákno provedlo konfliktní úpravu. To je možné pouze proto, že můžeme zajistit, že kód nebude mít žádné jiné účinky než úpravy státu (které můžeme šťastně opustit). To se provádí pomocí monitory STM (Software Transactional Memory) v Haskellu.
  • můžeme sledovat účinky kódu a triviálně jej pískovat, filtrovat všechny efekty, které bude muset provést, aby se ujistil, že je to bezpečné, a tak umožnit (například) živatelsky zadaný kód může být bezpečně spuštěn na web
0
Jules

Zlo je trochu přes vrchol .. vše záleží na kontextu použití jazyka.

Dalším hlediskem těch, které již byly zmíněny, je to, že značně zjednoduší důkazy o správnosti programu, pokud neexistují žádné funkční vedlejší účinky.

0
Ilan

Ve složitých kódových základnách jsou komplexní interakce vedlejších účinků nejobtížnější věcí, o které se domnívám. Mohu mluvit jen osobně vzhledem k tomu, jak můj mozek funguje. Vedlejší účinky a přetrvávající stavy a mutující vstupy atd. Mě nutí přemýšlet o tom, „kdy“ a „kde“ se věci stávají důvodem správnosti, nejen „co“ se děje v každé jednotlivé funkci.

Nemůžu se jen soustředit na „co“. Po důkladném otestování funkce nemohu dojít k závěru, že způsobí vedlejší účinky, které rozšíří atmosféru spolehlivosti v celém kódu, který ji používá, protože volající ji stále mohou zneužít tím, že ji volají ve špatném čase, ze špatného vlákna, ve špatném objednat. Mezitím funkce, která nezpůsobuje žádné vedlejší účinky a právě vrací nový výstup, je-li vstup (aniž by se dotkl vstupu), je téměř nemožné tímto způsobem zneužít.

Ale já jsem pragmatický typ, myslím, nebo alespoň se snažím být, a nemyslím si, že musíme nutně potlačit všechny vedlejší účinky na nejmenší minimum, abych zdůvodnil správnost našeho kódu (přinejmenším Považuji to za velmi obtížné v jazycích jako C). Tam, kde je velmi obtížné zdůvodnit správnost, je, když máme kombinaci složitých řídících toků a vedlejších účinků.

Složité kontrolní toky jsou pro mě ty, které jsou v přírodě podobné grafům, často rekurzivní nebo rekurzivní (fronty událostí, např. Které přímo nevyvolávají rekurzivní události, ale jsou v podstatě „rekurzivní“), možná dělají věci v procesu procházení skutečnou propojenou strukturou grafu nebo zpracování nehomogenní fronty událostí, která obsahuje eklektickou směsici událostí, která nás vede ke všem druhům různých částí kodebázy a ke všem vyvolávajícím různé vedlejší účinky. Pokud byste se pokusili načrtnout všechna místa, která nakonec skončí v kódu, bude to vypadat jako složitý graf a potenciálně s uzly v grafu, které jste nikdy neočekávali, by tam byli v daném okamžiku a vzhledem k tomu, že jsou všechna což způsobuje vedlejší účinky, to znamená, že vás nemusí překvapit jen to, jaké funkce se nazývají, ale také to, jaké nežádoucí účinky se vyskytují během této doby a pořadí, ve kterém se vyskytují.

Funkční jazyky mohou mít extrémně složité a rekurzivní kontrolní toky, ale výsledek je tak snadno srozumitelný z hlediska správnosti, protože v procesu nedochází k nejrůznějším eklektickým vedlejším účinkům. Teprve když složité kontrolní toky splňují eklektické vedlejší účinky, které mi připadají bolestivé, když se pokouším pochopit vše, co se děje, a zda vždycky udělá správnou věc.

Takže, když mám tyto případy, je pro mě velmi obtížné, ne-li nemožné, cítit se velmi sebevědomě ohledně správnosti takového kódu, natož velmi sebejistý, že mohu v takovém kódu provést změny, aniž bych zakopal o něco neočekávaného. Řešením je tedy buď zjednodušit kontrolní tok, nebo minimalizovat/sjednotit vedlejší účinky (sjednocením mám na mysli pouze způsobení jednoho typu vedlejšího účinku mnoha věcem během určité fáze systému, ne dva nebo tři nebo tucet). Potřebuji jednu z těchto dvou věcí, aby se můj mozek simpleton mohl cítit sebevědomě ohledně správnosti existujícího kódu a správnosti provedených změn. Je docela snadné si být jisti správností kódu, který způsobuje vedlejší účinky, pokud jsou vedlejší účinky jednotné a jednoduché spolu s řídicím tokem:

for each pixel in an image:
    make it red

Je docela snadné zdůvodnit správnost takového kódu, ale hlavně proto, že vedlejší účinky jsou tak jednotné a kontrolní tok je tak mrtvý jednoduchý. Řekněme však, že jsme měli takový kód:

for each vertex to remove in a mesh:
     start removing vertex from connected edges():
         start removing connected edges from connected faces():
             rebuild connected faces excluding edges to remove():
                  if face has less than 3 edges:
                       remove face
             remove Edge
         remove vertex

Pak je to směšně zjednodušené pseudokód, který by obvykle zahrnoval mnohem více funkcí a vnořených smyček a mnohem více věcí, které by musely jít (aktualizace více textur map, kostních hmotností, stavů výběru atd.), Ale i pseudokód je tak obtížné důvod správnosti kvůli interakci složitého grafického toku a probíhajících vedlejších účinků. Jedna strategie pro zjednodušení je tedy odložit zpracování a soustředit se pouze na jeden druh vedlejšího efektu najednou:

for each vertex to remove:
     mark connected edges
for each marked Edge:
     mark connected faces
for each marked face:
     remove marked edges from face
     if num_edges < 3:
          remove face

for each marked Edge:
     remove Edge
for each vertex to remove:
     remove vertex

... něco v tomto smyslu jako jedna iterace zjednodušení. To znamená, že procházíme data vícekrát, což rozhodně způsobuje výpočetní náklady, ale často zjistíme, že můžeme takto vzniklý kód mnohokrát číst snadněji, nyní, když vedlejší účinky a kontrolní toky přijaly tuto jednotnou a jednodušší povahu. Kromě toho lze každou smyčku učinit šetrnější k mezipaměti, než procházet připojeným grafem a způsobovat nežádoucí účinky, jak jdeme (např .: použijte paralelní bitovou sadu k označení toho, co je třeba projít, abychom mohli pak provádět odložené průchody v seřazeném sekvenčním pořadí pomocí bitmasků a FFS). Ale co je nejdůležitější, považuji druhou verzi o tolik snazší, o čem se zajímá z hlediska správnosti a změny bez způsobení chyb. A tak k tomu přistupuji stejně a aplikuji stejný druh myšlení, abych zde zjednodušil zpracování sítě jako zjednodušení manipulace s událostmi atd. - homogennější smyčky s mrtvými jednoduchými řídicími toky, které způsobují jednotné vedlejší účinky.

Koneckonců potřebujeme vedlejší účinky, které se vyskytnou v určitém okamžiku, jinak bychom prostě měli funkce, které vydávají data, kam nikam nevede. Často musíme něco zaznamenat do souboru, něco zobrazit na obrazovku, poslat data přes soket, něco takového a všechny tyto věci jsou vedlejší účinky. Ale určitě můžeme snížit počet zbytečných vedlejších účinků, které se dějí, a také snížit počet vedlejších účinků, které se dějí, když jsou kontrolní toky velmi komplikované, a myslím, že by bylo mnohem snazší vyhnout se chybám, kdybychom to udělali.

0
user204677