První způsob nevede na 100 funkcí, ale na 1000 funkcí, protože nikdo nebude zkoumat zda potřebná funkce už existuje a jak funguje, a rovnou si napíše vlastní. Map, filter a reduce k tomu přímo svádí.
V realitě to dopadne takto
x = map(a)
y = map(x)
z = map(y)
...
f = map(z)
f == a
Údržba reálného systému bude horror, stejně jako údržba jakéhokoliv systému, který využívá css a nebo nějaký šablonovací systém, kdy na 100 místech opravujete tutéž chybu, nebo provádíte potřebnou změnu, pak pracně testujete v aplikaci, zda jste ta místa nalezl všechna, časem se přijde na to, že ještě ne všude jste to opravil. V této situaci se do úpravy čehokoliv nebude nikdo pouštět, i malá úprava pak povede k rozsypání systému, protože se vždy na něco zapomene.
Není třeba zavádět FP, ale opustit přímé používání technologií jako css a šablonovacích systémů. To je příčina problému.
Naopak FP vede k pouzivani principu DRY mnohem vic, nez je to v tridnim OOP, kde se spousta spousta kodu porad opakuje dokola. Porad ty same pitomy smycky pri prochazeni kolekcemi atd., jak nemas funkce vyssiho radu (map, reduce, filter apod.), tak porad znovu a znovu pises skoro ten samy a prece ne presne ten samy kod. Akorat to "pekne" schovas do trid, takze zvenku to mozna vypada rozumne.
Neni problem. Asi nejtypictejsi priklad z Javy:
for (Object o : kolekce) {
neco_delej(o);
}
nebo podobne:
int suma=0;
for (int x : pole) {
suma+=x;
}
o kousek dal: suma*=x; atd.
nebo i variance na tema String.join()
To jsou prave ty malinke 3-10 radkove kousky kodu, ktery najdes v kazdem zdrojaku a jsou vlastne zbytecne, coz v Jave 8 nazorne ukazuje pouziti streamu a lambd (tj. FF konstrukci).
Taky si myslím, ale ono to jako nebude nikde jinde jiné. Navíc
1.si můžeš napsat 1 funkci třeba na sum a volat ji všude kde chceš ....
2. pokud na nějakém místě chceš udělat sum nebo cokoliv, tak tam tak jako tak nějaký kód zavolat musíš, i kdyby jsi to psal na toaleťák.
Jak se to jako bude lišit od funkcionálního? Stejně se ten sum provede, tak jako tak, akorát každá ta kolekce přes kterou iteruješ bude navíc další sada funkcí ....
Prvni si napises asi pres each nebo map, druhe pres reduce. V obou pripadech se pouziji funkce vyssiho radu (tedy FF pristup) a klidne se to da zabalit do tridy, to uz asi neni problem (opet narazim na Javu 8, ne ze by to byl nejaky zazrak, ale asi je znama nejvic). Kdyz to doplnis o immutable objekty a treba i lazy sekvence, mas vlastne i z OO jazyka pomerne pekny (neelegantni) FF jazyk :)
To je ono. OOP je PŘEDEVŠÍM o organizaci (statické i dynamické) kódu, potažmo entit systému. Funkcionální paradigma na mě dělá dojem, že je to o způsobu výpočtu. Takže je otázkou, zda jsou vůbec tato paradigmata k sobě alternativou a zda se nehodí pro jiné účely, osobně si ale zatím neumím představit v modelu obchodní aplikace náhradu OOP pomocí immutable FP. Třeba mi to někdo vysvětlí...
Vo funkcionalnych jazykoch sa obvykle pouzivaju moduly ako sady funkcii na kazdy ucel pozuivas inu sadu funkcii.
Urobis si trebars modul Stack s funkciami Stack.push Stack.pop Stack.top a potom volas nad listom funkcie z modulu Stack a pracujes s listom ako so zasobnikom na iny ucel si vytvoris inu sadu funkcii, je to omnoho flexibilnejsie ako OOP kde mas metody "pevne zviazane" s objektom.
Aha, takže místo objektu a metod, máš modul a sadu funkcí a když si na jiný účel vytvoříš nový modul a novou sadu funkcí, tak je to flexibilnější než vytvořit na jiný účel jiný objekt s jinou sadou metod?
Mimochodem před ten kód v http://pastebin.com/yafjn6Eb napsat clas {, přidat konstruktor, dopsat function a }{, uzavřít závorkou, tak je to normální třída.
V čem je to tedy lepší?
Modul nemá stav. Objekt mít stav může.
Tohle je docela velkou věcí v FP zvlášť v Haskellu - na spoustě míst nejde dělat spoustu věcí a je možné to přehledně řídit.
A to je docela fajn, protože já fakt vím, že ten modul stav nemá. Ty nevíš, jestli ten objekt ten stav nemá. Takže když se někdo rozhodne to třeba spustit ve víc threadech, tak ví, že to není problém. U objektu to nevím - autor možná stav nepoužil...
To nemusí být omezení jazyka. A co je pak speciální stav?
Když budu mít třídu - klasika - WriteLog která bude obsahovat private, nebo konstantu ROOT_PATH, tedy stejný pro celou aplikaci ať to zavolá kdokoliv a metody třeba writeToFile a clearLogs tak to přece není žádná chyba návrhu ani nedostatek OOP a žádné stavy se tam v podstatě nenachází, protože ROOT_PATH je vřdy stejná a neměná a funkce nezajímá jak je na tom log file.
A navíc, pokud je stav potřeba stav použít (tuny aplikací) tak mi nakonec z této výhody zbude akorát tak to, že mám modul místo class a psát bezstavově třeba v PHP dnes klidně můžu ....
Jako OOP mám rád, ale neberu to jako dogma, chci no minimálně rozšířit obzor, akorát se snažím tedy přijít na nějakou tu super výhodu, protože jak se to pomalu učím sám tak to by mi to taky mohlo trvat :-D a já radši všechno hned. . . . . . .
To byla rychlá jednoduchá trída, dále by mohla obsahovat private metody na zjištění data/přidání data, zjištění velikosti souboru....... jen na inlustraic toho, že objekt nemusí držet nějaký stav aby to byl validní OOP objekt a ne chyba návrhu.
"Tak pokud ta WriteLog nedrží otevřený soubor, tak je to právě jen takový namespace s jednou funkcí a konstantou. "
A co je potom ta funkce nebo modul z FP, když tedy nedrží stav? Taky je takový namespace s jednou funkcí ....?
"A co je potom ta funkce nebo modul z FP, když tedy nedrží stav? Taky je takový namespace s jednou funkcí ....?"
Funkce nebo modul? Já netvrdím to, že když je něco "takový namespace", tak je to špatně. Já tvrdím, že to není objekt ve smyslu OOP. Nevidím moc smysl třeba ve vytváření instancí objektů, které nemají žádný stav.
Ok.
O tom byl právě ten případ toho zapisovače. Je to jeden celek, který zajišťuje napříč celou aplikací nějakou "funkci" a nepotřebuje stav. Jde v podstatě o to, že tato funkcionalita reprezentovaná třídou může znamenat volání třeba 10 funkcí po sobě (uvnitř třídy) a ty se díky tomu, že náleží objektu a tam mají svůj smysl nemotají nikde jinde. Navíc ten objekt si může držet právě třeba root_path - která se za běhu aplikace nemění a tím ani nenese informaci o stavu, ale je tam, a vždy, kdyby jste nevolal objekt::metoda, ale jenom funkci, tak by jste tu konstantu musel vždy někde lovit. A že jich může být .
Dále taky agreguje funkcionalitu, takže je pak snazší ve funkcionalitě něco změnit (zde třeba metodu zápisu např. z po řádku na celý text atd ...) a nemusíte se bát, že se něco rozbije. Pokud by jste to vždy zajišťoval sekvencí na sobě nezávislých funkcí, tak je šance, že si tu sekvenci rozbijete a ještě k tomu na x místech.
Není příklad úplně nejideálnější, ale na vysvětlení, proč má smysl vytvářet objekty i bez stavu, to myslím stačí. Taky to může být něco většího.
Bohužel celá ta argumentace vůbec nevysvětuje, proč modul v Haskellu nebo F# by měl být horší.
Nehledě na to, že se doupouštíte chyby z neznalosti, protože v Haskellu neplatí: "Pokud by jste to vždy zajišťoval sekvencí na sobě nezávislých funkcí, tak je šance, že si tu sekvenci rozbijete a ještě k tomu na x místech." Samozřejmě, že si v Haskellu mohu tu sekvenci zajistit úplně stejně jako v OOP.
Takže spor je opravdu jen o stav.
„Nevidím moc smysl třeba ve vytváření instancí objektů, které nemají žádný stav.“
Mýlíte se. Vnitřně/implementačně tento objekt žádný stav nemá, ale navenek (díky zapouzdření+delegaci/volání neobjektové služby) reprezentuje instanci logu, jeho operace a stavy. A to v standardním, ortogonálním balení k celé aplikaci bez použití dodatečných mechanismů typu funkce ap.
To je tedy mega špatný návrh. Ještě chápu použití singletonů, ale vytvářet si víc instancí jakoby stejných, ale ne zcela stejných objektů (equals vs. =) je typická mutable OOP zhovadilost, která zcela nabourává představu, že entita je reprezentována svou hodnotou.
To už i ta Java raději používá například Runtime.getRuntime() a ne sémanticky nesmyslné new Runtime().
To je velmi podobne:
1) ty mas tridu s konstantou a mnozstvim (statickych?) metod, tudiz ani nevytvaris objekty, tj. instance te tridy. Ta trida tedy slouzi k tomu, ze konstanta a metody maji prefix MojeTrida.mojeMetoda()
2) alternativne mas namespace s konstantou a mnozstvim funkci
Konceptualne ani jedno z toho neni OOP a neni mezi tim velkeho rozdilu.
No, ono to asi OOP je, někdo to uznává, někdo ne, pro někoho prostě statická třída OOP není, nevím, celkem je mi to jedno, můj názor je že to OOP je, nebo statická třída minimálně do OOP patří, už jenom proto že nepatří jinam a vede mne k tomu i to, že třeba v Javě, která je silně objektová, tak tam taky je. Ale to tu neřeším a já to ani nezmýšlel volat staticky. Proč taky.
Ono totiž ta statická třída, to je spíš o způsobu volání a scope. Ale to je jedno.
Instance je normálně brána jako objekt. Pokud nevěříte -> UTG.
Řešil jsem, že modul a funkce v něm je v podstatě jako třída a funkce v ní.
Jen poznamenám:
"Ta trida tedy slouzi k tomu, ze konstanta a metody maji prefix MojeTrida.mojeMetoda()"
To je způsob volání v OOP. Tak snad se nebudeme uchylovat k takovému účelovému bagatelizování.
Tak dle mne to OOP není, protože třídy jsou definovány neortogonálně deklarativně k instancím a chovají se jinak, např. v Ckanálu nefunguje polymorfismus na třídní straně (v Javě asi taky, ale nezkoušel jsem). Java je zrovna příklad hybridního jazyku - má primitivní typy a deklarativní třídy, takže takový bastl. Je asi tolik silně objektová jako Zeman silným abstinentem.
Smalltalk jde na to mnohem chytřeji a jednodušeji: Používá metatřídní implementaci, kdy třídy jsou jen dalšími objekty, tudíž jsou dynamické (nic jako static obecně v čistém OOP neexistuje, protože neumožňuje pozdní vazbu) a platí pro ně polymorfismus jako pro každé jiné objekty. Zadarmo(!) k tomu díky ortogonalitě tříd a instancí dostáváte velmi silnou reflexivitu.
NO, možná by jste se divil, ale stran toho jak je to vtipné, tak on Zeman asi 10 let nepije kvůli špatnému zdravotnímu stavu.
Co je u Vás Ckanál nevím, asi se to píše v toaleťákovém editoru :-) ale jestli Java není objektová, tak už nic.
https://docs.oracle.com/javase/tutorial/getStarted/intro/definition.html
Jako samozřejmě primitivní typy najdete všude, teda krom splácanin kde je je všechno funkce a nebo všechno prohlásíte jako potomka typu 'objekt' ale nakonec ať děláte co děláte, když máte mít v proměnné interger, tak tam nakonec prostě někde je, ať je to obalené v čemkoliv.
Jinak, 'static' je modifikátor třídy a třída patří do OOP. Tečka. Nikde není psané, že existuje nějaké čisté a nečisté OOP, je jen OOP. Static je modifikátor určující vlastnosti třídy a ne příslušnosti k OOP. Ale možná se pletu, tak kdyby jste mi hodil link na čisté a nečisté OOP (nebo nevím jak, možná neOOP nebo poloOOP nebo jak to mu říkáte).
Smalltalk neznám ...
" tak on Zeman asi 10 let nepije kvůli špatnému zdravotnímu stavu."
2013
"ako samozřejmě primitivní typy najdete všude, teda krom splácanin kde je je všechno funkce a nebo všechno prohlásíte jako potomka typu 'objekt' ale nakonec ať děláte co děláte, když máte mít v proměnné interger, tak tam nakonec prostě někde je, ať je to obalené v čemkoliv."
Houby. Jsou to elektroni nebo fotoni. Zadna cisla.
"Jinak, 'static' je modifikátor třídy a třída patří do OOP. Tečka. Nikde není psané, že existuje nějaké čisté a nečisté OOP, je jen OOP. Static je modifikátor určující vlastnosti třídy a ne příslušnosti k OOP. Ale možná se pletu, tak kdyby jste mi hodil link na čisté a nečisté OOP (nebo nevím jak, možná neOOP nebo poloOOP nebo jak to mu říkáte)."
Static neni modifikator tridy, minimalne pokud se bavime o Jave. Neurcuje vlastnosti tridy ale metody nebo jineho clena.
Ciste a neciste OOP je rozdil mezi C++ a Smalltalkem.
"Smalltalk neznám ..."
Tak to aha. Jako bych vlastne ani nezacal odpovidat.
To má být jako co za televarieté?
"Static neni modifikator tridy, minimalne pokud se bavime o Jave. Neurcuje vlastnosti tridy ale metody nebo jineho clena."
Bavíme se o OOP, ne o jeho implementaci v Jave, nebo kdekoliv jinde.
Ale možná se pletu, tak kdyby jste mi hodil link na čisté a nečisté OOP.
To stále platí. Evidentně jste tady Nás slepé přiletěl zachránit, tak prosím, exhibice může začít .....
„ale jestli Java není objektová, tak už nic.“
Tak musím přiznat, že touto větou jste mi vyrazil dech. Tato věta, aniž byste to možná chtěl, docela přesně odhaluje, jak si představujete OOP. Plánuju se kouknout na to FP (ať konečně vím, vo co teda jako de), vy se koukněte na ten Smalltalk (nejlépe Pharo, stačí stáhnout a rozbalit), ušetříme si tím oba čas.
„Jako samozřejmě primitivní typy najdete všude...“
Nemám věru moc práce vyvracet vám vaše argumenty, stačí vždy uvést jako příklad Smalltalk.
„Jinak, 'static' je modifikátor třídy a třída patří do OOP. Tečka.“
Promluvil javař. Kde má Javascript ty třídy? Cože, Javascript není objektový...? Abych nezapomněl: Proč nemá Smalltalk static???
„Nikde není psané, že existuje nějaké čisté a nečisté OOP, je jen OOP.“
To nemusíte nikam psát, to vyplývá z věci. Výpočetní model založený výhradně na objektech je čisté OOP. Přidáte-li (primitivní) typy s imperativním výpočtem, tak už to není výhradně OOP, ale mix, protože využívá.
„Smalltalk neznám ...“
To jsem poznal z té věty o Javě.
"Tak musím přiznat, že touto větou jste mi vyrazil dech. Tato věta, aniž byste to možná chtěl, docela přesně odhaluje, jak si představujete OOP. "
S OOP jsem nezačal v Javě, dělal jsem v Javě a už zase nedělám, takže jsem si OOP věru na pár
jazycích vyzkoušel.
Stále nevidím link na čisté a nečisté OOP.
Ale beru, co není Smalltalk, jako by nebylo. Ok. Tím je to vše vyřešené. Se Smalltalk na věčné časy.
Předřečníci to asi myslí takto:
1) Java používá třídní OOP, což je jen jedna z variant OOP. Alan Kay by asi řekl, že ta horší varianta, ale tady záleží na preferencích každého soudruha :-)
2) Java v žádném případě není čistě OOP jazyk, což předřečníci správně řekli. Příkladem jsou primitivní datové typy, ty by v čistě OOP jazyce neexistovaly. Prostě například 1 by byl objekt (v třídním OOP instance třídy Integer), mohla by se mu poslat zpráva typu add(jiný int) apod. (zpráva=v třídním OOP omezeno na volání metody). Z toho plynou různé důsledky, mj. i prapodivný význam operátoru == a !=- apod. problémy s autoboxingem...
3) zůstaňme u operátorů. Pokud by Java neměla primitivní datové typy, celkem přímo by to vedlo k podtypovému polymorfismu a možnosti přetěžování operátorů (což jsou jen zajímavě napsané metody) a tím pádem i k mnohem větší flexibilitě jazyka. Příkladem může být NumPy, které do Pythonu přidává skutečná pole i operace nad nimi (selekci...). Aby to v Javě jo netahalo za oči, tak se alespoň přetížil opeátor + pro Stringy, jinak máš programátore smůlu, vocaď pocaď :-)
Jj, ano, celkem souhlas.
Akorát já nenapsal. že je Java čistě OOP jazyk, napsal jsem že je silně OOP, což se používá a znamená že OOP podporuje. Silně. Jsou i jazyky které ho nepodporují a nebo méně, či trochu jinak. O "čistém OOP jazyce" tady tady mluvili "předřečníci", ale co já tvrdím, jak chápu pojem OOP, že OOP je v podstatě soubor pravidel jak zapisovat a organizovat kód, není o tom, jak ho má každý jazyk implementovat, tzn, jestli i na třeba Integery použít objekty atd. aby se pak mohlo zaklínat tím, co je nějaké čisté nebo nečisté OOP, protože i třeba z historických důvodů se někde v jazyce vyskytuje bžilión normálních funkcí a já nevím čeho všeho mimo OOP. Třeba to PHP.
Lze na to jít i z druhé strany, zakázat si používat výrazy. Místo a + b napsat a.add(b), pak operátory (metody) můžete přetěžovat dle libosti, jen u základního aritmetického objektu povolíte konstrukce typu add(a) {sum([_value, a]} Takové akalkulické (disexpresní?) paradigma :-))) Výhodou bude přehlednost, srozumitelnost, debugrovatelnost, testovatelnost, modifikovatelnost, imutabilita. Typy vyřešíte v rámci akalkulické báze, která bude například v rámci metody add, řešit i typové konverze, na jednom místě v celé aplikaci :-))) Akalkulickou bázi budou tvořit funkce sum, mul, div, mod, sub, or, and, xor operující nad seznamy.
Já jsem ale nepsal o Haskelu. Psal jsem cosi OOP obecně. Jeho implementace do Haskelu mne nezajímá. To je věc Haskellu.
"Třídy patří do paradigmatu Typování"
Ano, patří. A typování se v OOP normálně používá pro definici proměných, tříd, ze kterých jsou vytvářeny objekty. Nevím jak je to v Haskellu a nezajímá mne to.
"Haskell má třídy, a je tedy podle vás OOP :-)"
Nic takového jsem nenapsal, ani nenaznačil. Ale jak jsem psal, Haskell mne nazajímá.
To se pořád točíte kolem implementace OOP v určitém jazyce.
"Kde vznikla ta predstava, ze OOP a tridy spolu nejak vyrazneji souvisi?
Nikdy jste nečetl o třídě a objektu v sousvislosti s OOP?
To "vyrazneji souvisi", které nic nevymezuje a dá se vyložit 1000 způsoby (asi připravený klacek na mě, který jako náhodou dostane takový nebo takový význam, hlavně oponující, až něco odepíšu), přeskakuji.
"Jsou OO jazyky bez trid a tridy sou bezne i mimo OO jazyky."
Ale stejně tak jsou běžně v OOP jazycích a nebo v jazycích, které OOP podporují. Takže nechápu co má ta věta jako tak celkem znamenat.
A pozor. objekt v některém jazyce nemusí být objekt z OOP. Např. Javascript.
"Ze te Haskell nezajima je dost smutne. Neni problem byt omezeny, ale snazit se zustat omezeny."
Každý podle svého. Mám povinností spoustu, dělám se spoustou technologií co se týče programování a k tomu mám vystudovanou automatizaci ... Mám dost práce se zlepšováním se v odvětví kde podnikám a s nasazováním nových postupů a technologií a díky automatizaci znovu oprašuji jeden asemblerový jazyk (pro PIC) a C/C++ a k tomu ještě testuji LUA a Clojure jestli se mi budou hodit. Tak se Vám strašně omlouvám, že mne Haskel a pár dalších jazyků nezajímá a omluvte mou omezenost.
"Objects in JavaScript are kind of two-faced.
From one side, an object is an associative array (called hash in some languages). It stores key-value pairs.
From the other side, objects are used for object-oriented programming, and that’s a different story.
In this section we start from the first side and then go on to the second one."
„A pozor. objekt v některém jazyce nemusí být objekt z OOP. Např. Javascript.“
Javascriptový objekt má identitu, zvládá skládání, zapouzdření i pozdní vazbu. To je plnohodnotný objekt. Schopnost přistupovat k vlastnostem jako v asoc. poli je pouze znakem výborně fungující reflexivity (což se např. u Javy říct nedá).
Píšu jeden větší projekt v haskellu a mám v něm 2 globální proměnné ("stav modulu"). Jedna je logging, druhá je cachování nějakého systémového volání. Proč? Protože FP přístup k loggingu je trochu overkill, když všechno, co potřebuju vypsat, je nějaká blbá hláška. Nicméně, logování je i tak hodně pěkný příklad, na čem ukázat ten rozdíl - akorát člověk pak pochopí, že se mu do toho úplně nechce, kvůli tomu, aby vypsal někam nějakou pitomou debug hlášku.
Takže jeden z FP přístupů k logování: "MonadLogger instance" obsahující nějakou funkci ("log") pro vypsání logu (trochu Haskell-specific, nevím, jak jsou na tom ostatní jazyky s monádama). Co to znamená? Pokud chci já v nějakém svém kódu použít logování, musí ta moje funkce běžet v nějakém "Monadu" (ono to tak není, ale tady v tomto případě se to dá představit jako "prostředí"). A to prostředí musí mít interface "MonadLogger", aneb implementovanou funkci "log". Pro různé typy monadu (prostředí) může být různá implementace funkce "log" - jeden typ např. může vypisovat natvrdo do stderr, druhý typ bude v "prostředí" držet stav "otevřený soubor", třetí typ bude všechno schovávat do nějakého seznamu a čtvrtý typ to všechno zahodí.
Všechny implementace funkce "log" jsou typicky bezstavové - vstupem je to, co chci vypsat a prostředí, výstupem je třeba nové prostředí. Jaké výhody mi tento model umožňuje? Např. když mě napadne, že chci logy z nějaké funkce místo vypisování zachytit a poslat uživateli přes síť, tak tu funkci prostě spustím v nějakém jiném "monad transformeru" (prostředí je na sebe možno vrstvit). Zkuste to napsat v OOP s globálním logging objektem tak, aby to bylo thread-safe.
Takže tolik k bezstavové implementaci logování. Jde to, je to výrazně lepší než OOP přístup, jenomže práce s Monad transformery není úplně příjemná, a spíš je to trochu příklad "over-abstraction", když jediné, co člověk potřebuje, je vypsat nějakou pitomou hlášku do logu. U větších projektů, které třeba pracují s vlastními Monady/Monad transformery to už ale takový "overkill" není.
Ale jo - ale ptal jsi se před tím na rozdíl - tak jsem ti to popsal. V modulu (normálně) nemáš stav, celé tohle logování principiálně nemusí nikde držet žádný stav (kromě "prostředí"), funkce využívající logování nemá přístup ke změně toho stavu, nemá přístup k tomu, co potřebuje funkce "log".
Všechno je to pěkně zapouzdřené a je to možné použít jako součást prostředí - tedy prakticky, jako kdyby to každá z volaných funkcí dostávala jako další parametr a případnou změnu prostředí vracela v návratové hodnotě.
Jo, aha, díky, takže jako když třeba v PHP nastavím nějakou "tu zlou" :-) globální proměnou - ta mi určuje stav a zároveň jakože vytváří prostředí a pak libovolně volám funkci zapouzdřenou v modulu a ta si "automaticky" (ale musí mít nějaký odkaz) bere to co chce z "prostředí", zde z globální proměnné, je tak?
Až na to, že ta globální proměnná není globální. Pokud bys implementoval ten MonadLogger, tak pravděpodobně způsobíš, že výsledný kód se přeloží do něčeho cca takového (záleží na implementaci toho monadu, takhle by to vypadalo pro State; Reader nebo Writer by byly jednodušší):
mujsoucet(int a, int b, Logger logger) { return (a + b, log(logger, ("Scitam " + a.toString() + " a " ++ b.toString()))); } errlog = FileLog(stderr); (vysledek, novylog1) = mujsoucet(1, 1, errlog); (vysledek2, novylog2 = mujsoucet(3,3, novylog1);
Takže žádná globální proměnná, immutable objekty, když tě napadne zavolat nějakou funkci s "jiným" logováním, tak klidně můžeš. Když se rozhodneš pro "žádné logování", tak se ti to přeloží tak, že ty parametry zmizí. No a konkrétně v Haskellu bys to pak používal cca takhle:
mujsoucet :: MonadLogger m => Int -> Int -> m Int mujsoucet = do log ("Scitam " <> show a <> " a " <> show b) return (a + b) runIOLogger stderr $ do mujsoucet 1 1 mujsoucet 3 3
P.S. Nemel by 'code' tag být pre-formatted???
> Jde to, je to výrazně lepší než OOP přístup, jenomže práce s Monad transformery není úplně příjemná, a spíš je to trochu příklad "over-abstraction", když jediné, co člověk potřebuje, je vypsat nějakou pitomou hlášku do logu.
OOP přístup je injectovat logger do objektů. Alternativně jde logger předat jako parametr funkce - což například ve Scale může dělat kompilátor automaticky. Další alternativou je dynamic scoping.
Jestli se dobře pamatuju, tak Scala je trochu FP a automatické předávání parametrů nemá s OOP vůbec nic společného (s FP nutně taky ne (Syntactic sugar), byť (->) Monad je v podstatě automatické předání parametru i bez syntactic sugar). Injektovaný logger do objektu připadá tak trochu jako "trochu lokálnější globální proměnná" (jak zajistit, aby ten samý objekt ve 2 threadech logoval různě?).
Ale jak říkám, FP přístup je na běžné věci trochu overkill (přechod mezi IO a "monad stack" je dost velký - ale v momentě, kdy už nějaký monad stack používám, tak je to dost triviální).
„...Injektovaný logger do objektu připadá tak trochu jako "trochu lokálnější globální proměnná"... “
Tak jestli to berete takhle, tak to pak jsou všechny objekty tak trochu lokálnější globální proměnné.
„...(jak zajistit, aby ten samý objekt ve 2 threadech logoval různě?)...“
Předtím by bylo vhodné vysvětlit, proč by měl vůbec objekt přizpůsobovat svoje chování tomu, v jakém vláknu běží.
> Jak zajistit, aby ten samý objekt ve 2 threadech logoval různě?
To může zařídit onen injektovaný logger. Nebo stále tu je dynamic scoping.
> automatické předávání parametrů nemá s OOP vůbec nic společného
To je pravda - mohu to tedy vyřešit elegantně bez monád i bez OOP.
> kdy už nějaký monad stack používám, tak je to dost triviální
Momentální problém monad transformerů je, že se dost těžko skládá kód, který používá různé monad transformery naskládáné v různém pořadí (jde to těžko i v situacích, kdy použité efekty komutují). Některé jiné techniky pro sledování efektů tyto problémy nemají.
„...Injektovaný logger do objektu připadá tak trochu jako "trochu lokálnější globální proměnná"... “
> Tak jestli to berete takhle, tak to pak jsou všechny objekty tak trochu lokálnější globální proměnné.
Nene, já toho o injekci zas tak moc nevím, ale pokud to dobře chápu, tak prostě při inicializaci objektu dojde k "injekci" toho loggeru podle nějakého pravidla. Takže v zásadě má pak nějaká část objektů v sobě injektovaný jeden logger, jiná část objektů jiný logger. Ale v principu těžko můžu v části kódu říct "a teď mi zavolej tuhle funkci a vrať mi, co zalogovala" - resp. můžu vyměnit ten logger, ale to pak není thread-safe.
>> Jak zajistit, aby ten samý objekt ve 2 threadech logoval různě?
> To může zařídit onen injektovaný logger. Nebo stále tu je dynamic scoping.
Co má dynamic scoping společného s OOP? (resp. můžete mi přesně vysvětlit, jakým způsobem byste to pomocí dynamic scopingu vyřešil? Když volám funkci "objekt.udelej_neco()" definovanou někde v jiném modulu, tak se do scopu toho modulu asi nedostanu.. nebo dostanu?)
A jak to zajistí injektovaný logger? (to by mě fakt zajímalo, protože o injection v OOP toho zas tak moc nevím)
> Momentální problém monad transformerů je, že se dost těžko skládá kód, který používá různé monad transformery naskládáné v různém pořadí (jde to těžko i v situacích, kdy použité efekty komutují). Některé jiné techniky pro sledování efektů tyto problémy nemají.
Souhlas. Na druhou stranu "instance MonadLogger" nemusí nutně namenat monad transformer.
Ale celé tohle píšu jako odpověď EOF, že v FP svým způsobem "vytvořit modul s jinou sadou funkcí" je skutečně často flexibilnější, než vytvořit jiný objekt s jinou sadou metod. FP prostě funguje jinak.
> Ale v principu těžko můžu v části kódu říct "a teď mi zavolej tuhle funkci a vrať mi, co zalogovala" - resp. můžu vyměnit ten logger, ale to pak není thread-safe.
Například logger se může podívat do globální proměnné, která je ThreadLocal, a podle toho pozná, co dělat. To je thread-safe.
> do scopu toho modulu asi nedostanu.. nebo dostanu?)
Právě, že můžete. Je to ale opět variace na globální proměnné (o trochu lepší). Příklad přímo z Practical Common Lisp (kapitola 6, část Dynamic, a.k.a. Special, Variables):
Thus, if you want to temporarily redefine *standard-output*, the way to do it is simply to rebind it, say, with a LET.
(let ((*standard-output* *some-other-stream*)) (stuff))
Je to thread-safe.
> Například logger se může podívat do globální proměnné, která je ThreadLocal, a podle toho pozná, co dělat. To je thread-safe.
Ano, může. Nakonec, proč se prasit s nějakým OOP, když máme globální threadlocal proměnné... když to nejde v OOP, tak staré dobré C pomůže.... tu infrastrukturu, aby práce s tím byla exception safe radši nechci vidět.
> Právě, že můžete. Je to ale opět variace na globální proměnné (o trochu lepší). Příklad přímo z Practical Common Lisp (kapitola 6, část Dynamic, a.k.a. Special, Variables):
Common Lisp je OOP? Mně to teda připadá jako variace na Reader/State monad, než ukázka toho, jak daný problém řešit v OOP....
> tu infrastrukturu, aby práce s tím byla exception safe radši nechci vidět
Osobně tam nevidím, žádný velký problém.
> Mně to teda připadá jako variace na Reader/State monad, než ukázka toho, jak daný problém řešit v OOP
Má to blíž ke globální proměnné. Můžete to nasimulovat pomocí ThreadLocal globální proměnné a funkce, která ji nastaví hodnotu, spustí jinou funkci a po skončení jiné funkce vrátí původní hodnotu.
> Common Lisp je OOP?
Řekl bych, že ano. V CL se často programuje imperativně - například se tam používají klasické cykly místo rekurze (kompilátory nemusí podporovat TCO).
>> tu infrastrukturu, aby práce s tím byla exception safe radši nechci vidět
> Osobně tam nevidím, žádný velký problém.
Jasně, do té doby, než zapomenete obalit "nastavLogger()" try/finally blokem.... dobře, tak pokud budeme mít OOP jazyky s podporou některých konceptů FP, tak můžeme použít lambda funkci...
> Má to blíž ke globální proměnné. Můžete to nasimulovat pomocí ThreadLocal globální proměnné a funkce, která ji nastaví hodnotu, spustí jinou funkci a po skončení jiné funkce vrátí původní hodnotu.
On ten Reader/State monad se dá taky nasimulovat pomocí ThreadLocal globální proměnné...
> Řekl bych, že ano. V CL se často programuje imperativně - například se tam používají klasické cykly místo rekurze (kompilátory nemusí podporovat TCO).
Mě připadá, že trollíte (možná jsem to měl poznat dřív?). Ano, lisp má CLOS, který je úplně jiný, než v jiných jazycích. Dynamic scoping taky v jiných jazycích nenajdete, a lisp je obecně dost "od všeho trochu"... takže je to evidentně ideální příklad jak ukázat rozdíl mezi hezky implementovaným loggingem v OOP jazycích a v FP... hlavně tohle byla odpověď na to, že v FP třeba logging nejde implementovat na rozdíl od OOP rozumně - a vy se mi tady snažíte vysvětlit, že v OOP jazycích to taky jde nějak podobně "občůrat", třeba použitím v podstatě globálních proměnných. Tak jasně, výjimky jsou taky k ničemu, pomocí "goto" to jde nasimulovat docela dobře.
> OOP jazyky s podporou některých konceptů FP, tak můžeme použít lambda funkci...
Ano.
Ale lambda funkce můžete také chápat jako objekty (například jako instance nějaké třídy Function). Jde jen o úhel pohledu. Stejně tak je to s moduly a objekty - když se podíváte na silnější systémy modulů a silnější systémy objektů, tak oboje splývá. Nebo někdo řekne, že Erlang je funkcionální jazyk a někomu přijde Erlang spíš jako zástupce OOP.
Nebo celé OOP jde vybudovat uvnitř lambda kalkulu a naopak lambda kalkul jde vybudovat pomocí objektů a tříd.
> On ten Reader/State monad se dá taky nasimulovat pomocí ThreadLocal globální proměnné...
To úplně ne (přestane to fungovat, když výpočet bude skákat mezi vlákny - typické například pro asynchronní výpočty).
> hlavně tohle byla odpověď na to, že v FP třeba logging nejde implementovat na rozdíl od OOP rozumně - a vy se mi tady snažíte vysvětlit, že v OOP jazycích to taky jde nějak podobně "občůrat"
Takovéto proměnné se běžně používají (což v .NET Frameworku vede k určitým potížím s asynchronními výpočty) - předkládám běžné postupy ze C#.
> To úplně ne (přestane to fungovat, když výpočet bude skákat mezi vlákny - typické například pro asynchronní výpočty).
Mát nějaký příklad, kdy lze State monad prothreadovat skrz asynchronní výpočty? Já mám zkušenosti docela s Haskellem, a tam bych stejně musel z toho State monadu nějak vyskočit, poslat stav na do jiného threadu, tam do něj zase vlézt. Takže pokud bych nastavoval threadlocal proměnnou někde v "runStateT", tak by to fungovalo i v tomto případě.
> Nebo celé OOP jde vybudovat uvnitř lambda kalkulu a naopak lambda kalkul jde vybudovat pomocí objektů a tříd.
Takže když si nějakou FP funkcionalitu implementujete trochu zaroh (protože ten design jazyka tomu moc nepomáhá) v nějakém OOP jazyce, tak pak můžete používat FP konstrukty. Ano. A proč byste to dělal, když programování ve stylu OOP přece tyhle problémy tak skvěle řeší a FP je na nic?
>> hlavně tohle byla odpověď na to, že v FP třeba logging nejde implementovat na rozdíl od OOP rozumně - a vy se mi tady snažíte vysvětlit, že v OOP jazycích to taky jde nějak podobně "občůrat"
> Takovéto proměnné se běžně používají (což v .NET Frameworku vede k určitým potížím s asynchronními výpočty) - předkládám běžné postupy ze C#.
Nějak se mi chce použít milou anglickou frázi: I rest my case...
> Mát nějaký příklad, kdy lze State monad prothreadovat skrz asynchronní výpočty?
Nestačilo by StateT aplikovaný na IO? To je totéž, jako bych stav předával parametrem - tj. stav přejde na jiné vlákno. Zatímco, když dám stav do nějaké ThreadLocal proměnné, tak na jiném vlákně budu mít jiný stav (stejné je to v Common Lispu - tam ony speciální proměnné jsou rovněž ThreadLocal).
Popisuje to mj. bod 5 Cons of Thread local storage na wiki.
> A proč byste to dělal, když programování ve stylu OOP přece tyhle problémy tak skvěle řeší a FP je na nic?
Že FP je na nic rozhodně neříkám. Zobecňováním konceptů z FP nebo OOP se oba styly k sobě přibližují (už jsem jmenoval blízkost modulů a objektů, dále to jsou například algebraické datové typy jako speciální případ podtypového polymorfismu (technicky to není totéž)).
> Nestačilo by StateT aplikovaný na IO? To je totéž, jako bych stav předával parametrem - tj. stav přejde na jiné vlákno
Ne. Komunikace mezi thready je v IO, takže se je potřeba ze StateT vyliftovat ven, poslat stav jinam a v přijímacím threadu to zase spustit přes runStateT nebo. nějaký "local". Takže threadLocal by určitě fungoval.
> Zobecňováním konceptů z FP nebo OOP se oba styly k sobě přibližují (už jsem jmenoval blízkost modulů a objektů, dále to jsou například algebraické datové typy jako speciální případ podtypového polymorfismu (technicky to není totéž)).
Já bych faktu, že "určité problémy lze v obojím elegantně vyřešit" asi nenazval "přibližují". Zrovna algebr. datové typy a polymorfismus jsou něco, co v kódu vypadá prakticky přesně "opačně" a z hlediska rozšiřitelnosti to má opačné vlastnosti - zrovna nedávno jsem napotvoru potřeboval u jednoho typu právě ty opačné vlastnosti a musel jsem to v haskellu implementovat přes Generics a vůbec mě to netěšilo.
> Ne. Komunikace mezi thready je v IO, takže se je potřeba ze StateT vyliftovat ven, poslat stav jinam a v přijímacím threadu to zase spustit přes runStateT nebo. nějaký "local". Takže threadLocal by určitě fungoval.
Máte pravdu v Haskellu to zřejmě bude, jak říkáte - nedošlo mi (dokud jsem si to nezkusil), že forkovací funkce berou IO x a nikoliv StateT s IO x. V jazycích, kde můžete funkci přerušit (například pomocí vymezených kontinuací) a pak pustit na jiném vlákně, se stavová monáda bude lišit od thread local proměnné.
> Zrovna algebr. datové typy a polymorfismus jsou něco, co v kódu vypadá prakticky přesně "opačně" a z hlediska rozšiřitelnosti to má opačné vlastnosti
Měl jsem na mysli to, že u data X = A | B | C můžete X chápat jako nadtyp a A, B, C jako podtypy. Toto chápání nám dovolí ADT modelovat v OO jazyce - X, A, B, C budou třídy a zakázáním vytváření dalších podtypů z X, A, B a C dostaneme ADT jako v Haskellu. Když podtypy nezakážete dostanete otevřený ADT, to umí například OCaml (samozřejmě umí i uzavřené):
type attr = .. type attr += Str of string type attr += | Int of int | Float of float
> Měl jsem na mysli to, že u data X = A | B | C můžete X chápat jako nadtyp a A, B, C jako podtypy. Toto chápání nám dovolí ADT modelovat v OO jazyce - X, A, B, C budou třídy a zakázáním vytváření dalších podtypů z X, A, B a C dostaneme ADT jako v Haskellu. Když podtypy nezakážete dostanete otevřený ADT, to umí například OCaml (samozřejmě umí i uzavřené):
Extensible variant types jsem neznal, dík za info. Nicméně to stejně neřeší to, že se k datům stejně budete chovat přesně reverzně než v OOP. Konkrétní příklad: mám JSON konfiguraci, a v ní mám list konfigurací nějaké služby. Datový typ toho listu budu v OOP modelovat jako interface/abstraktní třída "Služba", v FP z toho udělám datový typ "SlužbaA | SlužbaB | SlužbaC". Podsaď dobré, ale teď zkuste organizovat zdrojový kód tak, aby pokud možno všechna logika týkající se dané služby byla ve vlastním modulu. V OOP je to triviální, akorát někde musíte udělat "seznam služeb", aby se při načítání toho JSONu vyzkoušel parsing všech služeb. V FP je tohle velký problém, protože všechny vaše funkce dělají pattern match, takže budete mít kód distribuovaný podle funkcionality, nikoliv podle typu Služby.
Obě paradigmata umožňují problém "různé typy služeb" vyřešit - ale způsob, jak to řeší, je přesně opačný.
> Podsaď dobré, ale teď zkuste organizovat zdrojový kód tak, aby pokud možno všechna logika týkající se dané služby byla ve vlastním modulu.
Chápu, na co narážíte: pro každou operaci udělat metodu v bázové třídě (nebo v Haskellu udělat místo bázové třídy typovou třídu a pro každý podtyp udělat jeden typ) vs pro každou operaci udělat jednu funkci někdě mimo - např. pomocí visitoru/pattern matche.
Nicméně i tak lze ADT chápat jako omezenou formu podtypového polymorfismu (varianty ve skutečnosti nejsou typy, takže to není úplně přesné) - akorát, že typy tam nemají metody (u podtypového polymorfismu nejde o dynamic dispatch, ale o relaci býti podtypem - tj. nevadí, že typy nemají metody).
Ve Scale, kde varianty odpovídají třídám, lze snadno kombinovat oba přístupy.
To je nějaké složité :) Ne, jednoduse:
class Sluzba { virtual int vykonej_neco(); virtual int vykonej_neco_jineho(); };
a pak implementovat nějaké potomky a mít "Sluzba *seznam".
oproti
data Sluzba = SluzbaA ... | SluzbaB ...
vykonejNeco (SluzbaA{..}) =
vykonejNeco (SluzbaB{..}) =
vykonejNecoJineho (SluzbaA{..}) =
vykonejNecoJineho (SluzbaB{..}) =
Protože pak můžu udělat [Sluzba]. Pokud bych použil typeclassy, musel bych použít něco typu [forall (Sluzba a) => a], což při troše snahy a zapnutí pár extension použít jde, ale je to obecně považováno za FP anti-pattern.
> To je nějaké složité :)
Bohužel nevím přesně, na co narážíte? Nicméně zvolené řešení může záviset na tom, co chci přidávat (operace nebo služby).
> Pokud bych použil typeclassy, musel bych použít něco typu [forall (Sluzba a) => a]
Tohle řešení umožňuje přidávat další služby bez toho, abych musel upravovat existující funkce (neumožňuje ovšem přidávat funkce aniž bych změnil existující kód (instance typových tříd pro existující služby)). Jiná řešení (např. Datatypes à la carte pro Haskell) umožňují přidávat oboje bez měnění existujícího kódu).
> obecně považováno za FP anti-pattern
Existenciální typy jsou na druhé straně klíčem k zapouzdření a s jejich pomocí jde modelovat OOP v lambda kalkulu.
> Jiná řešení (např. Datatypes à la carte pro Haskell) umožňují přidávat oboje bez měnění existujícího kódu).
Dík za zajímavý odkaz (asi se někdu tu category-theory budu muset naučit). Docela zajímavé je, že ve výsledku ten jejich datatyp je skoro identický tomu, co vyleze z Generics. Takže vlastně používám skoro totéž, jenom na to jdu z opačné strany...
> Existenciální typy jsou na druhé straně klíčem k zapouzdření a s jejich pomocí jde modelovat OOP v lambda kalkulu.
To jo - ale je zvláštní, že je to považováno za anti-pattern a nedoporučuje se to používat....
„Co má dynamic scoping společného s OOP? (resp. můžete mi přesně vysvětlit, jakým způsobem byste to pomocí dynamic scopingu vyřešil? Když volám funkci "objekt.udelej_neco()" definovanou někde v jiném modulu, tak se do scopu toho modulu asi nedostanu.. nebo dostanu?)“
A to je přesně ten rozdíl mezi rádobypozdní vazbou a pozdní vazbou. Když to budete zkoušet v Javě, tak vám už překladač vyletí na chybu, že třídu objektu a jeho metody nezná, pokud ji nebudete mít odkázanou. Ve Smalltalku či Javascriptu na to sere pes, danému objektu se pošle zpráva s případnými parametry a ten ji buďto udělá, nebo vykotí chybu, případně neznámou zprávu zpracuje (v Javascriptu už to dnes jde hůř).
Unární funkce možná jo. Co binární funkce? Co funkce jako "průsečík", která bere objekty různého typu a přitom to není součást ani jednoho z nich?
Právě na matematické objekty je supr Haskellovský model, kde jsou oddělené instance a jejich abstraktní rozhraní a překladač je předává nezávisle.
Binární: leváKoule + praváKoule nebo leváKoule.spojenaS(praváKoule). Kde je problém?
Průsečík: Tak teď jste mi nahrál na smeč: Průsečík vzniká protnutím 2 objektů => první je příjemcem zprávy „průsečíkS“ s uvedeným druhým objektem jako parametrem. Protože 1. průsečík každých 2 typů objektů se počítá jinak a 2. je jiným útvarem, objektově to jde docela pěkně - každá třída útvaru má svoji metodu průsečikS, uvnitř rozvětvím zpracování na dílčí metody (průsečíkSPlochou, průsečíkSPřímkou, ...) podle třídy 2. objektu. Každá z těchto tříd vrací objekt nějaké třídy dle vzniklého průsečíku, např. Bod, Úsečka, Kuloplocha, ... Neboli zpracování každé kombinace 2 typů útvarů je systematicky rozčleněno do zvláštní metody, přičemž nemusím řešit, které 2 útvary se protínají a jako výsledek mi vypadává opět útvar (objekt!), se kterým můžu snadno dále pracovat. Co chtít víc...
Náhra na smeč? Je vypočet průsečíku trojúhelníku a úsečky metoda trojúhelníku, nebo úsečky? Nejde o to, že by to nešlo narvat do metody. Jde o to, že ta metoda se buď ad hoc přidá jednomu objektu, nebo bude ve více objektech metoda pro výpočet toho stejného. Pokud si nějak ad-hoc nevyberu privilegované typy, tak se mi navíc objeví cyklické závislosti.
U toho sčítání jde o to samé. Sčítají se rovnocenné entity, jenže v OOP vždycky musí být nejaký jeden objekt zodpovědný za výsledek. Takže je jeden z těch rovnocenných objektů třeba nějak privilegovat.
OOP blbě modeluje situace, ve kterých se nevyskytuje jedna entita, která za to zodpovídá.
Metoda je toho, koho se ptáte. Možná by vám pomohlo pojmenovat si metodu jako „můjPrůsečíkSÚtvarem“, pak není co řešit.
V OOP je běžné delegování. Když znám objekt, který něco umí spočítat, zadám mu to k provedení. Takže metoda stačí jen jednou u kerékoliv třídy z dvojice, druhá třída deleguje (neboli zde říká, že je to to samé, jako když se útvar z parametru ptá na průsečík se mnou). Takže žádné privilegované třídy (termín „typ“ je nevhodný, OOP nepracuje s (primitivními) typy).
Myslel jste vzájemnou závislost. Ta je mezi objekty zcela běžná (stejně jako mezi objekty reálného světa).
Sčítance jsou rovnocenné, proto je-li k tomu důvod, může objekt sčítání klidně delegovat na toho druhého), ale ta čísla stejně musíte nějak sečíst, a to tak, že k těm zleva začnete přičítat ty vpravo. To samé dělá ten objekt.
Původcem všeho (včetně výpočtu) v OOP je výhradně objekt, jiná výpočetní entita není, proto taky není nikdo jiný, kdo by mohl mít zodpovědnost.
Třída je přece typ. Není to primitivní, ale typ to je.
Tohle není příklad něčeho, co v OOP nejde udělat. Tohle je příklad, kdy je třeba z důvodů omezení OOP upravit návrh. Pokud se člověk nesoustředí na žádné paradigma, pak ten výpočet průsečíku nepatří ani do trojúhelníku ani do úsečky. Oba dva typy jsou úplně rovnocenné. Až OOP nutí jeden vybrat, přibalit výpočet k němu a ten druhý nechat delegovat.
Právě tím, že v OOP je původcem všeho (unikátní) objekt, je v OOP nutné upravovat návrh vždycky, když se někde vyskytuje víc rovnocenných entit.
Nechci tedy rýpat, protože jsme debatu posunuli jinam, ale jaký má význam objekt bez stavu? Tedy kromě toho, že o sobě říká "jsem instance této třídy" (což může být užitečné). Je nějaký příklad use case takového objektu? Jako toto mě skutečně zajímá, protože si myslím, že to jde proti všem patternům
Může třeba logicky shlukovat nějakou skupinu souvisejících sunkcí nebo nějaká logický prvek s tím, že má třeba 3 public metody na používání a několik protected/private na doplnění nebo z důvodu atomicity.
Třeba ten zapisovač logu do souboru: 1 jeden v celé instanci aplikace, několik funkcí k použití a jsou "viditelné" jenom ty které autor mínil používat a je to na jednom místě a zastřešuji tím nějaký účel a ne že si nadělám bžilion funkcí začnu je prát všude do modulů a pak když v ní chci něco změnit, tak nevim, jestli mi to někde neroze..re něco , kam ta funkce byla zamontovaná. No a nebo si můžu psát pořád nové ...to je ale k ničemu. No a v momentě kdy začnu vytvářet znovupoužitelné moduly a funkce, tak jsem tam, kde je dnes OOP,. ikdyž třeba neřeší všechno. Ono totiž to OOP taky kvůli něčemu vzniklo.
Netvrdím, třeba jak tu někdo psal Haskell na matematické výpočty, určitě, ale jako vyzdvihovat vlastnost bezstavovosti, když každá dnešní aplikace spravuje minimálně stav uživatele a toho co měl a dělal naposledy (i předminutou) je asi trošku mimo.
Jen tvrdím, že OOP není k ho.nu a má své velké místo na světě
Chápu. Ale to je v OOP přímo nechtěným jevem, znamenal by totiž rozdělení stavů a změn, takže data už nemají jasně definovanou, úplnou a ohraničenou množinu chování, ale na data můžu použít i chování, které jim nepřísluší (porušení konzistence). To je chyba! Spíš ta „flexibilita“ na mě opět dělá dojem dodatečného řešení chybně namodelovaného objektového systému. Pokud vím (pouze jsem četl, nezkoušel), tak na zabránění zdvojení kódu mají některé jazyky Traits, ale to je něco jiného, ty jsou natvrdo připnuté k třídě, takže instance nemůže libovolně použít kdejaké chování.
Paradigme neparadigma. Jde o to, že ať je to OOP nebo FP, tak na stejnou sunkcionalitu budou ty funkce prostě stejné.
class A {
public function soucet (a,b) {
return a +b;
}
nebo
function soucet (a,b) {
return a + b;
}
Ale OOP na rozdíl od toho definuje další vlastnosti celku - třídy - nevypisuji - znáš. A tím dodává další funkcionalitu, který se ano - někdy hodí, jindy je to jedno.
Jako kdyby mi někdo řekl třeba: Je to nové, je to FP, má to lepší správu paměti, třeba úplně jiný princip, definuješ to pomocí maker (jako třeba CofeeScrip), tak to zkracuje a hlídá syntax nativně, beru, ale argumenty o volání a zapouzdřování jsou dle mne liché a většina toho co jsem dnes četl vychází pouze z neznalosti OOP nebo ze specifik jednoho jazyka.
Ale jasne, tady jsme ve shode. Kde se lisime (asi :-) je tvrzeni, ze tim, ze jsi funkci dal do tridy, tak mas najednou to krasne kyzene OOP :-) Porad mas funkci (tedy asi tam chybi static, ale to si urcite rozumime), neni to nic, co by alespon v jednom jedinem bode splnovalo nektery ze znaku OOP, spis je to pure funkce z FP, kdyz jsme u toho. Ve smalltalku by se to resilo skutecne zpravou poslanou objektu a, ale to je uz tak moc OOP, ze by to vetsinu lidi nebavilo :-)
Co jsem chtel tim prikladem ukazat je prave to opakovani kodu - v OOP (javovskem pro Javu < 8) proste uvnitr metod musime pracovat imperativne, FF pristup tento stale se opakujici kod dokaze odstranit.
Na zaver: Java kterou porad zminuju, neni ciste OOP, takze cast tech problemu pripisme konkretnimu jazyku, to na 100% souhlasim.
Tak static tam chybí podle toho, jak a kde se má volat, ale čisté OOP asi není nikde, protože každý jazyk má nějaké ty Core funkce.
Jinak jako ta třída se mi nechtěla rozepisovat, to byl jenom takový ilustrativní příklad o volání funkce, ale to OOP právě řeší ještě i některé věci, které by se ..... už možná daly zařadit do nějakého návrhového vzoru.
Netvrdím, že svět musí být OOP, ale jako nakonec vždy na to imperativní programování nakonec dojde (nebo se na všecho dají udělat objekty a funkce a zase se to bude volat z jednoho), a pak už je to zase funkce vs. metoda, object vs sada funkcí, nebo jako multiply(a, b) vs. a*b spíš mi poslední dobou imponují preprocessory typu CofeeScript nebo SASS, na Javu už jsem pravda 1-2 roky nesahl. Nevím, Nějak tou dobou zrovna vyšla 8 takže v 8 se v novinkách moc nechytám a že tam byly. No, nebudu tvrdit že jsem master na FP, mohl by jste k tomu kodu z Javy ukazat jak by se to tedy lépe řešilo v FF?
Zkusim to dat z hlavy, taky me J8 uz miji (coz me ovsem az tak moc nevadi :-)
int sum = pole.stream().reduce(0, (a, b) -> a + b)
nebo si klidne muzeme tu funkci pro sumu vytahnout ven a pojmenovat, ovsem tady se narazi na omezeni semantiky Javy, takze radeji zustanme u lambdy. Ta vsak neni to hlavni, hlavni je funkcionalni reduce, tedy funkce vyssiho radu, tedy typicke FF.
Ten prvni priklad bude kolekce.forEach(name -> nejaky vyraz) coz neni az tak funkcionalni (side effect), nebo kolekce.map(funkce/lambda) nebo kolekce.filter(....).
FP má horší správu paměti, protože imutable. Z hlediska správy paměti je neměnná datová struktura to samé co objekt, navíc musí řešit logickou, ne nutně fyzickou existenci mnoha kopií téhož. Což je stejné jako u OOP, u OOP se sdílí kód, u FP se sdílí data (kopie téhož = sdílení), ale obě paradigmata patří do téže kategorie z hlediska přidělování paměti.
Výhoda FP je možnost přirozeného paralelního běhu, například map můžete zpracovávat současně na více procesorech, protože jednotlivá volání funkce jsou na sobě nezávislá. Takže to v principu může zajistit VM automaticky.
Takže nějaké budoucí VM FP jazyka může obsahovat nejen GC, ale i třeba automatický load balancer pro běh v cloudu, nebo na vícejádrovém stroji.
"FP má horší správu paměti, protože imutable." ne nutne, mnohdy spis naopak. Ze je neco immutable znamena v dusledku to, ze nikdy nemusite klonovat objekty (coz je naproti tomu operace velmi casto pouzivana v OOP jazycich, i kdyz existuje par patternu, ktere ji 'zatracuji' :)
Ale z diskuse je videt (nemyslim primo vas), ze si pod pojmem OOP a FP kazdy predstavuje neco jineho, coz je dusledek toho, jak si ty terminy ohnuli tvurci jednotlivych jazyku (C++ar bude mit uplne jiny nazor nez clovek od Smalltalku nebo Erlangu napriklad, dtto na strane temne sily FP :-)
Co vidime v poslednich letech je prorustani mnoha FP myslenek i do takzvanych "OOP jazyku" jako je Java (ta ma sice k plnemu OOP dost daleko, ale to je na jinou debatu, asi nekde u piva :). Protoze OOP a FP jsou ortogonalni pristupy, takze se nevylucuji.
Jediný rozdíl mezi for a map je v tom, že u map máte zajištěno, že volání těla cyklu jsou na sobě nezávislá, co má význam pro multiprocessing. Cyklus for pomocí map můžete napsat
map(function(i) {
return i + 1;
}, data);
nebo
function add(i)
{
return i + 1;
}
...
map(add, data);
Což je potencionálně nebezpečné, protože při změně funkce add, musíte studovat kontext použití funkce add v map, protože funkci add použijete na více místech a používání map vede k tomu, že kontexty použitých funkcí jsou jen zdánlivě podobné a liší se v maličkostech, což se pak odráží v nemožnosti používat jednu funkci v podobném kontextu, ale svádí to k tomu.
Cyklus for můžete taky napsat jednořádkově
for i in range(1, 10): add(i, data)
[quote]Což je potencionálně nebezpečné, protože při změně funkce add, musíte studovat kontext použití funkce add v map, protože funkci add použijete na více místech a používání map vede k tomu, že kontexty použitých funkcí jsou jen zdánlivě podobné a liší se v maličkostech, což se pak odráží v nemožnosti používat jednu funkci v podobném kontextu, ale svádí to k tomu.[/quote]
Teda kromě toho, že vůbec netuším, co by vámi napsaný kód měl dělat mi vůbec není jasné, co tímhle myslíte. Nějaký praktický příklad by nebyl? Jako logicky, pokud napíšu funkci "add", která něco sečte a používám to v kontextu "potřebuju něco sečíst", načež jednoho dne se rozhodnu, že to místo sčítání bude násobit, tak mám asi problém. Jen nechápu a) proč bych to dělal a b) co to má společného s FP?
add je "proměnná", můžete si místo ní dosadit jakoukoliv jinou funkci, představuje blok kódu v cyklu.
Místo ní si představte funkci, která vrací datum, cílový formát je d.m.Y a v jiném kontextu náhodou potřebujete d-m-Y a tato změna je provedena v časové tísni při řešení krizové situace. Takže, když tuto změnu chcete provést, musíte projít kontext všech volání této funkce. Další možnost je přidat transformaci výsledku původní funkce do požadovaného tvaru, která ale znepřehleňuje a zaneřáďuje kód. Navíc, když se někdy rozhodnete, že systémový formát data bude d-m-Y místo původního d.m.Y tak budete muset navíc odstranit všude ty ad hoc transformace.
Ale to opat nie je problem jazyka/paradigmy, ale programatora, ktory si kod rozhadzal, zneprehladnil a zaneradil.
Mimochodom, ak uz chces robit funkciu, ktora vrati datum, tak logicky a samozrejme dovolis nastavit cielovy format cez argument funkcie... Cize tvoj priklad je chyba v navrhu, nie v paradigme.
Něco jiného je univerzální knihovna, něco jiného konkrétní aplikace. Dovolíte-li nastavit formát data, budete mít v aplikaci v různých kontextech různé formáty data. Časem se dojde ke konfliktu mezi nimi. To bych nedělal. Formát data je zase "proměnná", můžete si za něj dosadit třeba formát ID dokladu.
???? Tak normální postup snad je udělat si funkci ve stylu 'mujFormat = formatDate "d.m.Y"', a když v jiném kontexty potřebuju "d-m-Y", tak použiju rovnou "formatDate "d-m-Y"". A teď mi není jasné, kde je problém.... pokud potřebuju na "některých" místech změnit "mujFormat" na něco jiného, tak to na těch místech asi budu muset přepsat na "mujNovyFormat". Protože jsem při návrhu kódu jaksi nepočítal s tím, že by se ty formáty měly lišit.
Ale vůbec nechápu, co to má společného s FP. OOP to snad nějak vyřeší?
Bez urážky, teď se Ti povedlo nádherně demonstrovat perverzitu extrémního OO myšlení. Datum je přece jenom jedno, je to trojice rok, měsíc, den a formátování, resp. parsování jednoho nebo druhého formátu není snad žádný důvod k tomu. aby člověk vytvářet 10 tříd. Natož "konverze mezi formáty", to je věc importu do vnitřní reprezentace a exportu ven. Tudíž, když budu chtít zůstat u OOP, tak si přece nebudu dělat 10 tříd jenom proto, že existuje 10 způsobů formátování data, ale udělám si jednu a konverze ať řeší metody.
Neřekl bych, že jde o extrémní OO myšlení. Alan Kay by asi řekl, že jde o extrémní nepochopení OOP. Ale jinak souhlasím. A takovýmto způsobem je vytvořeno 90% objektových návrhů - a to je to, co dělá z OOP sémě satanovo. Chyba není v OOP. Chyba je v tom, že ho spousta lidí vůbec nepochopila, včetně autorů spousty učebnic i některých programovacích jazyků, jako třeba u C++.
V každém případě - ten výše uvedený "elegantní" příklad je zrůdnost a ukázka, jak se to právě nemá dělat. Ale dobrý tip, na co se zeptat u pohovoru.
"atarist"
"Ivan Nový"
Z toho se mi to ale jeví tak, že budu muset pořád vymýšlet nové a nové názvy funkcí, nebo je předávat přes parametry jako bezejmenou funkci, ale to by vedlo na neustálé opisování. Jako můžu to kopírovat, nemusí to být problém, ale není to výhoda.
A pokud nebudu, tak budu muset mít globální namespaces nebo všechno includovat v podstatě jako třeba v oddělením překladu ...
Jako ta výhoda, že to v podstatě nativně dokáže běžet na multitask je super, jem mi ten kód moc kratší než ten Javový moc nepřipadal, a to se tu řešilo jako jeden důvodů. Já ne. Nemyslím si že kvalita programátora nebo jeho díla se dá soudit podle délky kódu, už jenom kvůli čitelnosti. To ať to radši projede nějaké preprocessor.
Děkuji Vám za příklady a asi to v tomto případě udělám tak, jak to radil Bruce Lee: "Nejdříve se techniku nauč a pak ji klidně nepoužívej"
Dále v diskusi jsem přidal odkaz na přednášku autora knihy, ten ukazuje snad vhodný kompromis, tak jak o tom píše v článku pan Tišnovský, i když ne tak explicitně.
Já jen polemizoval s prodejním argumentem pro FP, který je v nadpisu příspěvku. Ono i prodejním argumentem pro CD v počátcích bylo, že ho můžete nosit v kapse spolu s klíči a nepoškrábe se :-)))
NO, tak jako prodejné hesla, to je jasná věc. Mě šlo ale spíš o to, já jsem začínal HTML+CSS. Pak Matlab a Simulink, průběžně za ta léta C / C++, microC a Assembler pro PIC, Java + Android Java -> to bylo nějakých pár let, ale ve všem jsem tvořil projekty, ne že bych to jako jen tak zkusil nebo prošel tutorial a nyní se několik let živím webem PHP JS CSS SASS jQUERY MYSQL ............ Klasika a začal jsem nedávno zase oprašovat znalosti v C / C++ a nyní se poslední dobou jak houby po dešti objevily, nebo jako znova se vrací na výsluní funkcionální programování a tak jsem se chtěl dobrat toho, jak moc velký má smysl se tomu věnovat, jestli to za rok třeba nesplaskne a jak moc velký to má potenciál......
Každopádně děkuji a na ten odkaz se určitě podívám .....
Samozřejmě že než něco napíšu, tak prozkoumám, jestli už to napsáno nebylo, jak tě proboha napadlo že to zkoumat nikdo nebude? Co je to za blbost? Všechno co plyne z tvého mylného předpokladu tudíž samozřejmě nemá žádnou váhu.
Navíc např. v Haskellu se to zkoumá velice snadno. Vše je funkce, a díky prohledávači Hoogle můžeš snadno vyhledat funkce podle požadovaných typů, a v (staticky typovaném, to není příklad např. Clojure) FP se při programování většinou následují typy (potřebuju funkci, která filtruje, tj. musí vzít kolekci prvků typu A, predikát z A do Boolean, a výsledek musí být kolekce A. Zkus si to zadat do Hoogle , a hnedka uvidíš nalezené funkce filter, dropWhile, takeWhile). Umí tohle tvůj OOP jazyk? A u kterého jazyka myslíš, že je pohodlnější hledat lego kostičky pro znovupoužívání?
Nebudete zkoumat co už napsané bylo, nemáte k tomu dostatek sebekázně, nikdo k tomu nemá dostatek sebekázně. Hovoříme tady o tisících funkcí. Například v matematice existují důkazy, které jsou tak rozsáhlé, že na světě neexistuje člověk, který by daný důkaz četl a pochopil celý. A to bude budoucnost FP.
Můj OOP jazyk to umět nemusí, protože danou metodu v celé aplikaci použiji jen párkrát. Díky dědičnosti, skládání a abstrakci.
Mnoho tříd má implementovanou metodu display, ovšem volání této metody, ve tvaru například app.display() je v ideálním případě v celé aplikaci jen jednou.
Lego kostičky pro používání je jednoznačně jednodušší vytvářet v OOP paradigmatu. U FP to reálně platit nebude, protože vytvořit novou funkci je méně pracné, než vytvořit třídu, proto funkce budou tvořeny ad hoc, bez rozmyslu, a to má jasné důsledky. FP nevede ke "kostičkování", ale ke vzniku amorfní hmoty, navíc komplikované nelokalizovanými strukturními závislostmi vyplývajícími ze skládání funkcí.
Dále je mnohem jednodušší otestovat stav, než dokázat, že daný soubor pravidel generuje to, co si myslíme, že generuje.
Ak si uvedomíme, že tu existuje náboženský fanatizmus, tak môžeme pripustiť aj programátorský fanatizmus. Tam by som vieru vo všeobímajúce riešenia cez OOP zaradil.
Zacitujem z iného vlákna: "Soudím tak podle kódu některých open source projektů, se kterými přicházím do styku a sleduji jejich postupný vývoj k lepšímu. Od chaosu, k OOP řádu."
Napríklad Magento je super príklad vrcholného OOP řádu, ktorý dospel až do takého molochu, ktorý bez 5x silnejšieho servra, ako iné projekty plus navyše šaškovania s cachovanim nepobeží. A tá radosť vývojára, keď má riešiť JEDINÚ drobnosť na TROCH miestach.
Ale řád je řád a je na prvom mieste a až potom je tu projekt a potreby trhu.
Nie je náhodou WordPress ako najpoužívanejší open source CMS systém iba slabo líznuty OOP?
Otázka je, jak rychle do takového stavu dojde FP, troufám si tvrdit, že rychleji, než OOP. A nebude jednu věc řešit na 3 místech, ale na 50. Budete mít funkce dphLevaBota, dphPravaBota, cenaBoty, cenaObuv, ...
Jinak k OOP se masově přešlo ve stavu, když se psaly v C, či Pascalu, nebo BASICU funkce bota_uloz(), bota_nacti(), bota_zobraz(), aby se vůbec vědělo, co s čím souvisí. Nyní budete psát uloz(bota(i)), nacti(bota(i)), zobraz_sablona_xy(bota(i)), nebo snad zobraz(sablona(j), bota(i)), a nebo sablona(zobraz, bota(i)) ? A to vám nějak pomůže?
V podstatě není důvod, aby něco podobného nastalo, já mám spíš už z toho pojmenování (dphPravaBota...) pocit, že se někdo snaží napasovat imperativní kód na funkcionální paradigma. aniž by tušil jak na to :-)
Jinak ta kniha je přesně o tomto - o změně myšlení, ne nutně o změně na jiný jazyk a jinou syntaxi.
Mám tomu rozumět tak, že se snažíte dokázat, že když je jeden objektový (skutečně?) projekt na hovno, tak je na hovno celé OOP?
Jednu věc na třech místech? To ale nevypadá na chybu paradigmatu, ale návrhu.
Skvělý (skutečně?) Wordpress není objektový - další důkaz, že je OOP na hovno?
Co jste vlastně svým příspěvkem chtěl říci?
Tak to si potom vybral špatné argumenty. Přece to, že někdo něco nějakým nástrojem zkurvil nebo nezkurvil, není důkazem o kvalitách nástroje. Kdyby řekl: Tohle s tím nejde, protože něco, tak je to něco jiného. To je pak diskuse jak noha. Všichni jsou odborníky na objektové paradigma, ale jestli jsem se potkal v životě s 2 lidmi, kteří alespoň trochu vědí, o co jde (a že bych rád), tak je to moc. Tak kde se v těch diskusích „bevou“?
Máte pravdu, väčšina objektových fanatikov tomu nerozumie a iba sa modlí k objektom. Tí čo tomu rozumejú to používajú a neevanielizujú.
Moja skúsenosť je taká, že ak úspešný projekt prejde od funkcii k OOP kodu, tak ide u používateľov do kytek pre výkon, alebo neohybnosť. :D Pretože tomu začnú šéfovať fanatici.
DATA kašlú na objekty.
Tam je asi problém objektových fanatikov, ktorí asi nepatria medzi tých 2, ktorých ste stretol. Že v prvom rade sa modlia objektom a až potom datam/užívateľovmu času/hw/ďalšiemu vývoju. Potom sa stane, že neobjektový WP je na polovici publikačných webov, čiže úspešnejší, ako všetky ostatné objektové monštrá.
Není spíš fanatické, říkat, že když někdo dělá objektově a dává mu to smysl, tak že je fanatik a "modlí sa k objektom"? To samé by se dalo napsat o ostatních a to je přece blbost. Až na vyjímky samozřejmě.
( To je úplně stejná blbost, jako když se furt hádají prorusáci a proameričani a navzájem si nadávají že ti druzí jsou váleční štváči a přitom obě skupiny řvou, že se má ta druhá vyhladit, a že ta jejich je správná a mírumilovná )
Ale vůbec spousta závěrů je chybná, třeba:
"Potom sa stane, že neobjektový WP je na polovici publikačných webov, čiže úspešnejší, ako všetky ostatné objektové monštrá."
1. Polovina WP je v objektech, ale to nerozhoduje o tom jestli je úspěšný. O tom rozhoduje to, že je tu dlouho, takže je ho spusta, že má aktualizace na kliknutí, že má podporu, že jsou tam dořešené všechny hovadiny až po barvu adminu a taky to, že je po instalaci ihned připraven k použití.
2. Vidíte, půlka jsou objekty a jak je oblébený. No samozřejmě to s tím nemá co dělat.
"DATA kašlú na objekty."
Ale kašlou i na všechno ostatní, data jsou prostě data, nějaké jednotky informace. Pokud je propojíte s kódem, fajn - buď přímo v interpreteru a nebo třeba jako Doctrine je propojí s objekty - takže ono to data na objekty kašlou je pravda jenom z omezeného pohledu.
Dál pokračovat nebudu, nechce se mi ....
No a nebo jako FP fanatici. To ale přece nikam nevede, a už určitě se nedá říct, že je OOP k ničemu.
Taková slova mi připomínají jeden údajný výrok předsedy patentového úřadu, tuším na začátku 20.stol.(když tak mne omluvte, historická data jsou pro mne nezapamatovatelná), který prohlásil, že už bylo vše objeveno.
K tomu WP, naopak postupem času kus WP přepsaly do objektů.protože to má své výhody.
Jinak nevím jak moc je Doctrine ukecaná nebo ne, ale hezky ilustruje, že "data ví o objektech", pokud je jim to dáno, takže takový výtka byla, no přinejmenším, bezpředmětná.
Tvoje OOP vede k tomu, ze casem nikdo nevi, jaka varianta metody se zrovna pouzije, a tak si radsi nadeklaruje klidne totez znova, aby si byl jistej, ze to bude ta, kterou zrovna potrebuje.
Jako bonus, protoze metody a data jsou propojene v ramci objektu, to vede k tomu, ze kdyz nekdo potrebuje stejnou funcionalitu nekde jinde, musi ji tak jako tak znovu napsat, protoze primo zavolat ji nemuze.
Bonus cislo dve je pak ten, ze v ramci uzasnych moznosti OOP sezere int mega ram. Protoze zaroven ma 30 vsemoznych metod, ktery sice nikdo nikdy nepouzije, ale co kdyby, ze ...
Funkce ma navic jednu uzasnou vlastnost - je porad a za vsech okolnosti stejna. Pokud je vstup int a vystup float, tak to tak bude at uz ji zavolam kdekoli. V pripade metod to tak neni.
" OOP sezere int mega ram. Protoze zaroven ma 30 vsemoznych metod, ktery sice nikdo nikdy nepouzije, ale co kdyby, ze ..."
mas dojem, ze kod zabira v pameti tolik mista?
"Funkce ma navic jednu uzasnou vlastnost - je porad a za vsech okolnosti stejna. Pokud je vstup int a vystup float, tak to tak bude at uz ji zavolam kdekoli. V pripade metod to tak neni."
motas staticke typovani s cistotou funkce
Všechno to co jste napsal, je záležitost nepochopení OOP a špatný návrh. Nic víc.
Mimochodem funkce pro každou proměnou můžete použít třeba v PHP klidně dnes, ale je to roky prověřený přístup na nic a každý na to nadává a hrabat se v tom není sranda. Viz, půlka Wordpresu. Nedej bože zase někde narazit na "global".
Naopak OOP třeba nabízí zapouzdření, což je dost důležitá vlastnost programovacího jazyka a třeba takový JS, který by se mohl FP celkem podobat, tak tam se po OOP volá několik let. Jako po opravdovém OOP, ne simulovaném pomocí uzavírání do funkcí a anotování aby se to tak tvářilo pro kompilery alá Typescript.
OOP má jednu klíčovou vlastnost, kterou se nepodařilo žádnému jinému strukturovanému programování dosáhnout, a to rozdělení problému do izolovaných domén (objektů), čímž je dosaženo snadnější udržovatelnosti zmenšením a omezením problému. Právě špatná architektura aplikace bývá nahrazována obecněji dostupnými daty/funkcemi, v nejhorším případě globálními, kdy všechno může všude (systém kámen šuká cihlu).
S Javascriptem začínám, ale dle mého je objektovější než kdejaký jiný jazyk. Nezvykem je prototypování, jinak jde o poměrně přímočarý jazyk s triviální syntaxí a velkými možnostmi (přesný opak mainstreamových jazyků). Zkuste nadhodit, co je v něm špatně.
"OOP má jednu klíčovou vlastnost, kterou se nepodařilo žádnému jinému strukturovanému programování dosáhnout, a to rozdělení problému do izolovaných domén (objektů), čímž je dosaženo snadnější udržovatelnosti zmenšením a omezením problému."
To řeší/řešilo už modulární programování a vlastně obecně použití knihoven popř. ještě jmenných prostorů. Btw bez jmenných prostorů je i OOP docela rychle na hraně, to je asi další důležitá součást jazyků.
" Btw bez jmenných prostorů je i OOP docela rychle na hraně, to je asi další důležitá součást jazyků."
Není. Stačí prefixovat své třídy svým prefixem. Třeba místo class A { ... napsat class WP_Class { ...
Není to nic neobvyklého, uznávám že to není ideální, ale pokud chcete nasazovat třeba více, a cizích, tak je to nezbytné. To se ale týká všech jmen mimo jmenné prostory, i funkcí.
Úplně stejný jak když se budou muset volat funkce aaa_BBB__ccc__xxx () a aaa__BBB__ooo__xxx().
A nebo se to dá do modulů, ty budou mít bez namespace podobný tvar a budou se volat moduly a z nich funkce, místo tříd a jejich metod.
no, prostě buď bude mít každá funkce nebo třída nebo modul unikátní jméno a nastane to pojmenovávací peklo a nebo maespaces.
Ale ve Vašem příkladě je právě špatně, správně by to bylo:
Atarist_Class_A {}
|____ Atarist_Class_B {}
|_____Atarist_Class_C {}
A pak dohrajete můj modul:
EOF_Class_A {}
|_____ EOF_Class_B {}
|_____ EOF_Class_C {}
No ale ty namespaces jsou lepší ;-)
Ajo tak já to myslel trošku jinak. Řekněme, že mám aplikaci nazvanou RegistrEET :))) , ta má GUI a já chci použít pěknou implementaci tlačítka od EOF. To je asi typický příklad pro OOP, kde excelule, protože dobře popisuje situaci a navíc je GUI tak jako tak stavové.
Takže mám udělat:
class RegistrEEE_OkButton extends EOF_CommonButton {}
nebo (pořád při absenci namespaců):
class EOF_OkButton extends EOF_CommonButton {}
?
(uff no ještě že namespacy, navíc hierarchické máme :-)
Máte mít jen class Button { function __construct(Action $action, $format=ButtonFormat) {} }
a class Action { function __construct(url) {} } a class FormFactory s metodou createOkButton() a do konstruktoru této třídy předáte RegistrEEEActionBuilder, pokud nelze udělat nějaký univerzální a data načíst z konfiguračního souboru.
Jejdanánky, taková neznalost...
„...jaka varianta metody se zrovna pouzije...“
Ta z nejnižší úrovně hierarchie dědičnosti, v tom je právě ta jednoduchost a systematičnost specializace a jí odpovídajícímu chování.
„...metody a data jsou propojene v ramci objektu, to vede k tomu, ze kdyz nekdo potrebuje stejnou funcionalitu nekde jinde, musi ji tak jako tak znovu napsat, protoze primo zavolat ji nemuze.“
a) Daný objekt řeší nějaký problém, proto je vhodný pro řešení daného problému i na jiném místě aplikace. Chybou struktury aplikace je nedosažitelný jinde.
b) Objekt obsahuje funkcionalitu, která je obecnější, proto má být z něj vyčleněna jinam a objekt má delegovat požadavek.
„...zaroven ma 30 vsemoznych metod, ktery sice nikdo nikdy nepouzije...“
a) Metody, které se nepoužívají, je vhodné odstranit.
b) Jedná-li se o knihovnu, jde o běžný kompromis, kdy není možno si vybírat, které metody začleníte do aplikace (i když např. v takovém Smalltalku je možno metody oddělit do balíčku).
c) Vzhledem k tomu, že kód metod se v raměti vyskytuje jen jednou, jde o zanedbatelnou část běžící aplikace.
„Funkce ma navic jednu uzasnou vlastnost - je porad a za vsech okolnosti stejna. Pokud je vstup int a vystup float, tak to tak bude at uz ji zavolam kdekoli. V pripade metod to tak neni.“
Přiznám se, že tuto úvahu ne úplně chápu, pravděpodobně máte na mysli, že když je metoda stejného názvu ve 2 objektech, nemusí dělat to samé. Toto je přesně podstata objektu - má svoje chování odpovídající jeho potřebám bez ohledu na objekty ostatní. V OOP NEEXISTUJE globální funkcionalita, ta se vyskytuje pouze lokálně v objektech. Mimochodem přistrkovat funkci data je typický imperativní model výpočtu, kde původcem děje je operace. V OOP je původcem děje vždy objekt, který má nějaké chování, proto pro něj globální funkce či funkce z druhé strany aplikace nemá smysl.
Mám velmi vážné podezření, že jde u vás o nepochopení OOP.
Oosbně jsem zatím nic lepšího než OOP na modelování obchodních systémů neviděl a pořád čekám, až mi někdo ukáže něco lepšího. Zatím marně.
"Lego kostičky pro používání je jednoznačně jednodušší vytvářet v OOP paradigmatu. U FP to reálně platit nebude, protože vytvořit novou funkci je méně pracné, než vytvořit třídu, proto funkce budou tvořeny ad hoc, bez rozmyslu, a to má jasné důsledky."
[citation needed]
To je snad provokace, nebo co? Běžně se setkávám s obrovskými projekty, kde většinu kódu tvoří bordel v podobě setterů, klíčových slov a anotací bez dokumentace.
OOP strašně svádí k tomu, vytvářet cosi, co funkčně není nic moc, ale protože je to třída, tak je to hrozně moc správné.
Ve skutečnosti už v těch jazycích máme funkce připraveny a otestovány pro použití. Například nebudu odbíhat od mého oblíbeného Clojure: http://clojure.org/api/cheatsheet
Většina funkcí a maker zmíněná na té stránce se týká základních datových struktur: seznam, mapa, množina, vektor + abstrakce ve formě sekvencí (patří sem i zippers a funkce xml-seq)
Je toho už implementováno tolik z prostého důvodu - většinu problémů vyřešíš jednou, dvěma funkcemi, třeba spojenými přes threading makro. To nemyslím teoreticky, ale i prakticky, prostě je toho už připraveno mnoho a velmi to ušetří práci.
Práci to sice ušetří, ale znemožní údržbu reálné aplikace modelující svět mimo oblast IT, protože sice bude silná abstrakce algoritmů, ale slabší abstrakce vnějšího světa. Svět budete popisovat tak, jako když ho popisujete slovně, to znamená, že často budete opakovat fráze. Aplikace bude mít povahu textu, význam bude ukotven k danému místu v textu. Nebude tvořen prefabrikáty, ty totiž zůstanou na úrovni pouze základních pojmů jazyka (seznamy, fronty, sekvence), nikoliv představ a modelů vnějšího světa (produkt->auto->dveře).
No ono je nas asi vice nejspis protoze hodne z nas nezije a neprogramuje osamote v bubline.
Ja celkem verim, ze mate disciplinu ale vemte si treba meho kolegu, mizerneho to programatora takrka v kazdem smeru, ktery mi prozradil za naskakuje do FP vlaku, protoze v tom jsou lepsi prachy a mensi konkurence. Regulerni invaze barbaru.
Jak je na tom třeba, když to vezmeme, javascript? Tam je zase v podstatě všechno objekt, ale za ten se může schovat i funkce a dlouho už se řeší, jak tam opravdové OOP chybí, protože je to taky otázka znovupoužitelnosti, zapouzdření a reflektování entit, se kterými pracuje a které ne náhodou reprezentují entity reálného světa.
Ptám se, protože jsem se o jazyk Closure začal zajímat nedávno a určitě tam budou potřeba nějaké návrhové vzory a implementace pro velké, nebo široké projekty a OOP je toho v podstatě takovým mezi-řešením. Zatím co jsem měl možnost vidět jen kusy, ale ještě jsem neviděl, jak to bude fungovat v celku.
Děkuji
Ano, to je casty mytus, ze pomoci OOP namodelujeme i objekty realneho sveta. Pro takove ty skolni priklady, jako "Pes je zvire", "Pes steka", tudiz class Pes extends Animal (mno uz tady by se nekdo zarazil) a public void zalezDoBoudy(), private boolean jeVBoude jakoby skutecne popisuji realny objekt tridy Pes. Problem je, ze to nejde efektivne napasovat na vsechno, hlavne ty vazby, to je nejvetsi maso.
Proto taky mnoho OO projektu skoncilo zacmodrchanou pavucinou objektu, ktere mezi sebou nejak komunikovaly.
Aby se tento velky (vlastne uz od samotneho pocatku nutny) problem resil, zavadi se navrhove vzory, ktere vlastne rikaji: "OOP jako takove k dobremu produktu nevede, tady mas par otestovanych templatu, jak vlastne s OOP pracovat".
+ navic programatori resi a budou resit schizma mezi OO pristupem a databazemi. Mezitim si neuvedomi, ze nejvetsi problem je ve zpusobu prace se stavem v aplikaci. Jak toto neni reseno rozumne, tak se proste aplikace vyviji se spoustou chyb. FP toto docela elegantne resi.
doklad
- interni
- dodací list
- faktura
- dobropis
- pokladní doklad příjmový
- pokladní doklad výdajový
- platební příkaz
- výpis z účtu
- příjem na sklad
- výdej ze skladu
- interní účetní doklad
- externi
- dodací list
- faktura
- dobropis
- pokladní doklad příjmový
- pokladní doklad výdajový
- zařízení
- fronta
- sklad
- událost
- ...
- akce
- ...
- entita
- adresa
- ucet
- sklad
- produkt
- zakaznik
- dodavatel
- vyrobce
Už jen těmito objekty spolehlivě namodelujete chování střední a menší firmy
"Ivan Nový"
Souhlas. Dělám to často. Ale zmršit se to dá lehce :-) . Jako perfektní případ, kdy se OO přístup parádně povedl, bych uvedl Nette, samoozřejmě 100% OO juice to není, ale tak 99,5% určitě.
Podle vlákna kde jsem byl nedávno, nebylo by ideální postavit OOP na FF základu?
Super, konecne priklad. Takze se podivejme na praxi - zrovna tady a presne tady narazis na ORM, tedy na dva odlisne svety, ktere ty stejne objekty z realneho sveta popisuji rozdilnymi zpusoby. Je nejaky lepsi? Kdyz ano, proc se nepouziva vsude? A skutecne vadi tak moc, ze v DB najednou vidime interni stav tech objektu a zadne metody? A premyslel nekdo nad tim, jak by se vlastne cely model sveta zmenit tim, ze by ty zazamy byly immutable a tudiz u kazdeho bylo casove razitko? To je totiz model Datomicu. Nerikam, ze je nejlepsi, ale je jiny a meni zpusob chapani realneho sveta.
Protože to, že se nějaký "engine" stará o paralelní přístupy k entitám už tady máme v databázích a co z toho plyne? Buď se o to stará programátor, nebo engine, ale pak tu máme třeba takový nepěkný jev jako je Dead lock. Dají se nastavovat úrovně, ale nakonec to stejně musíte vyřešit v aplikaci, protože nemůžete třeba uživateli napsat : "Ukládám" a pak to neuložit .... A to se ani nebavím o synchronizaci nebo dolování dat odtud ....
Asi mysli zaznam=record neboli v reci DB radek=row. Je fakt, ze Datomic takto pracuje, opatruje zaznamy casovym razitkem a vlastne posouva paradigma popisu sveta trosku jinam, nez to je v relacnich databazich. Jak je to uspesne, to se chystam nekdy vyzkouset na jednom projektu, v nemz se prave s 'historii' pracuje.
„...presne tady narazis na ORM...“
Na ORM narazíte pouze v případě, že se rozhodnete pro persistenci relační databází, což je pro objektový systém to nejhorší, co můžete vybrat. Takže nevytvářejte tady dojem, že to jinak nejde.
„Je nejaky lepsi?“
Pro obchodní aplikace je OOP zrovna vymyšlené.
„Kdyz ano, proc se nepouziva vsude?“
Protože všichni umějí z průmek jen C++ + RDB (stačí se podívat do zdejší debaty, kolik lidí OOP chápe).
„A skutecne vadi tak moc, ze v DB najednou vidime interni stav tech objektu a zadne metody?“
Je to něco jiného, než když máte objektovou databázi, kde ona sama pracuje se stejným modelem jako aplikace, neboli aplikace jsou jen distribuované objekty, všechno je jen jednou, je to jeden systém.
„A premyslel nekdo nad tim, jak by se vlastne cely model sveta zmenit tim, ze by ty zazamy byly immutable a tudiz u kazdeho bylo casove razitko?“
To se běžně v aplikacích modeluje, říká se tomu historická/časová data, historie, historizace, ... Objekt si vede historii svých stavů.
„Proto taky mnoho OO projektu skoncilo zacmodrchanou pavucinou objektu, ktere mezi sebou nejak komunikovaly.“
Nebo taky kvůli něčemu úplně jinému, že? (Jmenuje se to chybný návrh modelu.) Neplácejte paušálně, nadhoďte příklad.
„...navic programatori resi a budou resit schizma mezi OO pristupem a databazemi...“
Schizma by si mohli ušetřit, kdyby se od relačních databází přesunuli alespoň k dokumentovým, lépe objektovým, akorát se do toho nikomu nechce, tak čemu se všichni diví?
„FP toto docela elegantne resi.“
Jak???
Sice jsem už slyšel o realizaci ODB pomocí relační databáze, ale to je asi nějaký bastl. Koncepční implementace je přes objektové prostředí (s virtuálním paměťovým prostorem) na serveru s distribucí objektů klientům a jejich konkurencí. V případě, že uvážíte, že např. ve Smalltalku jsou třídy taky objekty, pak je možno je ortogonálně bez jakéhokoliv dodatečného mechanismu uchovávat v DB a distribuovat, čímž vzniká jakýsi distribuovaný objektový systém. To je zároveň odpovědí na první otázku - objektové DB nemají s relačními nic společného.
Zkušenost s nimi nemám, kdysi jsem se o to pouze zajímal. Nasazeny jsou omezeně - lze dohledat u jednotlivých výrobců.
Pěkný den, já bych se chtěl zeptat (možná by to mohl být článek) na aplikaci funkcionálního jazyka ve vztahu k Domain Driven Design, které je obecně spojované s OOP, nebo obecněji, které části a v kterých vrstvách lze snadno (je lepší) nahradit funkcionálním přístupem..., které naopak mohou být navrženy objektově...
Skoda, ze v knizce chybi Haskell, protoze to je "enfant terrible" v FP. To ji v mych ocich dost degraduje.
Byl jsem dlouho odpurcem FP, ale nakonec jsem skoncil (po kratkem koketovani se Scalou a Clojure) u Haskellu. Dlouho jsem premyslel nad tim, cim se FP a OOP lisi, a co to znamena pro architekturu. Nakonec, diskuse s kolegou o OOP, znamenala pro me prulom v teto otazce.
Moje soucasna domnenka - OOP a FP se lisi v pojeti rozhrani mezi castmi programu, a ve zpusobu zapouzdreni dat. V OOP jsou rozhranim volani metod objektu, tedy akce, ktere ma cilovy objekt provest. Data se pritom snazime zapouzdrit do objektu. Naproti tomu v FP jsou rozhranim samotna data (v podstate typy).
Svym zpusobem jde o dualni pristup. V OOP predavame v ramci casti programu data zapouzdrena v objektech, v FP predavame (opacnym smerem) funkce, ktere nad temi daty pracuji. IMHO kazdy design pattern v OOP lze prevest do FP tim, ze misto predavani objektu budeme predavat funkce opacnym smerem. Take se na to lze divat jako na zmenu vztazne soustavy - v OOP jsou funkce na miste a data se pohybuji, zatimco v FP se pohybuji funkce a data stoji na miste.
Proto se tak FP uplatnilo v MapReduce paradigmatu (a jeho naslednicich), ktere je cele o tom, predavat mezi distribuovanymi uzly operace k datum misto samotnych dat. A v podstate i puvodni Unixove paradigma rour je dost funkcionalni, akorat ta datova rozhrani jsou ad hoc, vetsinou textova.
Myslim, ze pristup FP je lepsi, protoze:
- Data se lepe semanticky definuji nez operace. Pokud jsou tedy rozhranim data a ne operace, je snazsi tomu rozhrani porozumet.
- Data jako rozhrani davaji vetsi prostor ke znovupouzitelnosti. Nad objektem, ktery data skryva, jsme omezeni akcemi, ktere dovoluje jeho rozhrani. V FP ale k danym datum muzeme provadet libovolne operace. To je hezky videt prave na Unixovem pristupu, ktery je asi nejlepsim uspechem modularity v pocitacich vubec.
- V OOP se casto stava, ze potrebujete data prece jenom predat, a to pak znamena spoustu operaci, ktere jsou v podstate identitou. V FP tohle odpada, protoze je zrejme, ze nekde predavate identickou funkci.
To neni moc podstatne, v kazdem paradigmatu potrebujete nejakou kombinaci funkci a dat. Spis jde o to, jak se pracuje se "zdroji" dat.
Nejlip tenhle rozdil vynika v Haskellu, ale objevuje se do jiste miry ve vsech FP jazycich. V OOP se zdroje dat skryvaji do malych jednotek, objektu. To co se predava mezi castmi programu jsou prave odkazy na zdroje. V pure FP (jako ma Haskell) to tak neni, tam si zdroje drzi centrum, ktere misto toho dostava instrukce ve forme treba monadickych typu.
Obecne se z toho pak stava rozdil ve zpusobu, jakym se pracuje s menitelnym stavem (coz je v podstate forma zdroje dat). V OOP se ten stav zvlada tim, ze se schova pod nejake API operaci nad nim. Tim je mozne ten stav nekam jinam predat, zmenit vlastnika, aniz by se narusila jeho konzistence. V FP se stejny problem zvlada tim, ze se ten stav nikam nepredava, ale misto toho se nad nim operuje primo prostrednictvim dodanych pozadavku, jak ho zmenit.
No, asi se to tak trochu da brat, ale v (pure) FP nejde jenom o oddeleni stavu, ale vsech vnejsich akci, od operaci.
Krome toho, nelze to chapat tak, ze to v FP zpusobuje nejaky "chaos" navic, jak to vnima pan Novy - alespon v Haskellu je to co si muzete s danym datovym typem dovolit striktne omezene jeho typem. Skrze typovy system lze skryt i implementacni detaily, samozrejme.
Hm. A jak naimplementujete dohromady identitu a mutabilitu (běžné vlastnosti skutečných entit), když každá změna entity znamená ve FP vytvoření její neidentické kopie? Nebo jinak: Čím je dána identita entity ve FP?
(Čekal jsem, že mi odpoví spíš pan Uživatel si v profilu nezvolil přezdívku.)
Řekl bych, že zdánlivou kopií, předávat se budou de facto jen změny originálu. Například, je-li vstupem číslo 8, implementované to bude (int, [], 8), tak výstup bude po přičtení jedničky (int, [(add, 1)], 8) a výsledek se bude počítat teprve, když už to bude nezbytné, pomocí eval((int, [(add, 1)], 8))
Jde o to, ze by se entita nemela menit. Doporucuju (pokud vas to skutecne zajima) si precist a zamyslet se nad timto claneckem: http://www.clojure.org/about/state Tam se tedy spise mluvi o identitach.
Rich o tom mel nekolik prednasek, zkusim dohledat, jestli bude zajem.
V programování existují podle mě dva přístupy. První je "nějak to uděláme a bude to fungovat" a druhý je "pokud pojedeme podle specifikace a neuděláme logickou chybu, program bude fungovat správně". Reprezentantem prvního (imperativního) přístupu je třeba OOP - autonomní objekty se nějak dohodnou a udělají to dobře. Reprezentantem druhého je třeba čisté FP s tím, že systémy jako Agda a Coq se dokonce snaží (mimo jiné) dosáhnout toho, že správost algoritmu nebo dokonce programu lze prokázat-
Nevýhodou OOP je, že se velmi těžko bezpečně určuje, co daný program dělá, protože je psaný v zásadě metodou "zdola nahoru", postrádá referenční transparentnost, protože je pmlný objektů s měnitelnými stavy, které jsou ke všemu autonomní a dělají si, co "považují" za správné. Z pragmatického pohledu to může být často výhoda, ale principiálně si myslím, že je lepší přístup FP a že vývoj tomu dává pomalu zapravdu.
Teď vůbec nehovořím o problémech s paralelními výpočty, to je podle mě jenom vedlejší efekt.
„...protože je psaný v zásadě metodou "zdola nahoru"...“
Můj ty Tondo... Původní OOP naopak díky pozdní vazbě je vhodné na výstavbu shora dolů a doporučuje ji! To, že to nejde v bastlech typu Ckanál, Java či Chnushnus, je problémem jejich implementace OOP a časné vazby, díky které nejde nekompletní aplikace spustit!
Já chápu, že výpočetní model beze stavů je jednodušší, ale má jednu dost podstatnou chybu - je k hovnu. A objekty (stejně jako funkce) považují za správné jen to, co do nich dáte, FP za vás taky programovat nebude.
Pozdní vazba v tomto smyslu prakticky nic neřeší. Naopak umožňuje původní návrh, který mohl nakrásně být čistý a psaný shora dolů, v průběhu vývojového cyklu hezky rozbít.
FP za nikoho programovat nebude, ale bezestavovou/non-IO část programu lze verifikovat a mít jistotu, co dělá. To je u OOP utopie. A samozřejmě má každý praktický funkcionální jazyk způsob, jak ty čisté části propojit s reálným světem tak, aby to nebylo k h*vnu. Za mě je OOP výrazem rezignace, FP je neustálé hledání cesty k ideálu.
Tyhle příspěvky nepíšu proto, abych se hádal, který přístup je lepší. Ale chtěl jsem napsat svůj pohled na nevýhody OOP; tvářil ses, že Tě to zajímá.
> správost algoritmu nebo dokonce programu lze prokázat
Pro OO jazyky existují nástroje pro verifikaci - např. KeY pro Javu.
> Teď vůbec nehovořím o problémech s paralelními výpočty, to je podle mě jenom vedlejší efekt.
Paradoxně v praxi je s paralelními výpočty ve funkcionálních jazycích často větší problém než v imperativních jazycích.
Viz například už starší zápisek Parallel generic quicksort in Haskell.
On quicksort je ve své podstatě imperativní algoritmus. Funkcionálnímu přístupu daleko víc odpovídají věci typu mergesort. Bohužel se ve funkcionálních jazycích dá jednoduše slepit něco, co trochu jako quicksort vypadá, takže jeden z prvních příkladů bývá tenhleten fujtajxl.
S paralelními výpočty je problém tak nějak obecně. Ve FP jsou ty problémy akorát trochu jiné. Zásadní problém je dělení stavového prostoru na vhodný počet podobně náročných částí. Tohle je často stejně náročný problém, jako to prostě spočítat sériově. A to na paradigmatu nezávisí.
Dávat jako příklad QuickSort je trošku nešťastné. Každopádně jsem psal, že FP hledá cestu tam, kde OOP (IP) rezignuje na vývoj. Že ta rezignace může být někdy pragmatická, nepopírám. S tou Javou je to poměrně dobrý protipříklad, ale Java myslím není zrovna etalon objektovosti podle SB...
Neřekl bych, že rezignuje na vývoj.
Když se podíváte mimo mainstreamové jazyky, tak uvidíte velmi zajímavý vývoj. V kontextu této diskuze je například zajímavý jazyk ParaSail (od lidí z AdaCore). Je to OO jazyk, který je thread-safe (podobně jako Rust) a navíc má implicitní paralelizaci.
Nebo v dnešní době jsou populární aktory a mikroslužby, což jsou vlastně objekty.
Ano, mainstreamové ("OOP") jazyky se vyvíjejí hlavně díky tomu, že si berou (logicky, v OOP paradigmatu není co brát) inspiraci ze světa FP jazyků. A ten Parasail vlastně také (buit-in and inherently parallel map-reduce z příkladu na hlavní stránce jazyka) To je samozřejmě super.
Nevýhoda OOP je v tom, že ak sa začne programátor modliť OOP a nie užávateľovi aplikácie a HW, tak sa často super aplikácia zmrší. OOP fanatik sa viac venuje OOP návrhu, ako odladenosti aplikácie. Kľudne zmrší super fungujúci projekt, či už ho spomalí, alebo zabuguje, len aby bol dokonale objektovo naprogramovaný. Ako už bolo niekde tu v diskusii, alebo v článku spomenuté, vyplodí sa mrte kodu, ktorý spraví to, čo by spravili pipes v konzole na jeden riadok. A čím viac kodu, tým často väčšia chybovosť.
Ja myslim, ze FP lepsi je (viz moje argumentace). Protoze i ten objektovy pristup lze do znacne miry emulovat (treba pres typove tridy, nebo jinak, i kdyz je pravda, ze v jazycich, ktere nejsou tak striktne FP jako Haskell, jdou nektere ty veci hur).
Ale hlavne, je otazka, proc bys chtel mit v programu "entity, ktere neco delaji a dava smysl je o neco pozadat"? (Jak uz jsem psal vys, uz samotna tahle myslenka je zpusob mysleni v OOP.) Treba v Haskellu se tohle da resit pres monady, tedy ze fakticky vracis program, ktery rika, o co se ma pozadat a jak.
To druhe mi prijde vyhodnejsi, protoze nad tim programem muzes nejak pracovat, treba ho interpretovat uplne jinak - jsou to data. Jenom samotna volani ale tezko.
Je to asi trochu jako rozdil mezi GUI a prikazovou radkou. GUI ti nedava moznost napsat skript, ktery neco dela, vsechno se dela primo.
No to záleží, co v tom GUI je. Když vezmu Eclipse IDE tak mi dává jaksi větší možnosti než příkazová řádka.
Ale celá argumentace ohledně FP se točí kolem modulů a jakéhosi volání záhadných funkcí které mají něco spasit, ale pokud chci napsat nějakou funkcionalitu, tak musím nějaký kód vytvořit a jestli ho obalím do metody třídy (OOP) nebo modulu (FP) je úplně jedno. V každém případě budu volat obal::metoda (možná jen jiná syntax) a uvnitř kód: zápis, čtení, inkrementace ............. Fajn. FP je hned nad daty, ale když chci, můžu třeba v Doctrine nebo v Javě Hibernate (mapování dat na objekty) tak k tomu můžu taky lézt přímo a změnou objektu, čí zavoláním jeho funkce pracovat přímo s daty a reflektovat jejich změny.
Tak kde je tedy to jasné zlepšení?
Zlepšení by mohlo být v omezení co s těmi daty můžete dělat, jak je měnit a můžete jen map, filter, reduce. Prosíváte data a na základě vyloveného vrátíte pozměněné kopie.
Na vstupu dostanete adresu kolekce, na výstup uložíte adresu pozměněné virtuální kopie vstupní kolekce. Funkci je jedno z jakou kolekcí pracuje, přístupy k položkám, můžete dodat ve formě funkce, jako parametru.
Místo vytváření abstrakcí ve formě objektů - zákazník, objednávka, vytváříte abstrakce ve formě činnosti - sum_order(type, key, time_range) -> sum_order_items(orders(time_range), id(type, key), name, quantity), kde orders je funkce vracející objednávky, kde id je funkce, která vrací funkci, která otestuje zda aktuální prvek je daná objednávka, další parametry jsou funkce, které vrací funkce, které vrátí požadované hodnoty z řádku objednávky, takže na této úrovni nepotřebujete žádné datové typy a činnost je popsána zcela obecně. Všechny parametry jsou funkce.
Vyhoda FP je prave v te centralizaci pristupu k datum.. (lepe receno asi v tom, ze data jsou rozhrani, jak uz jsem psal) Ze stejneho duvodu se OOP aplikace tak casto svazuji s databazemi, protoze je to proste snazsi (klasicke relacni databaze jsou velmi ne-OOP, jelikoz maji centralni schema). To "distribuovani a skryvani dat" napric mnoha objekty v ramci OOP aplikace je sice jistym resenim modularizace programu, ale ma svoji cenu - vetsi potize pri pristupu k datum. FP ale ukazuje, ze muzeme mit vyhody obojiho - jak modularitu, tak pristup k datum. Toho se dosahuje kombinaci immutability a laziness, jak o tom pise klasicky clanek "Why functional programming matters". I kdyz samozrejme ma to svoji cenu, tou je vyssi abstrakce a vic prace pro kompilator (zatim ne zcela vyresene problemy).
Tak právě ty relační systémy bych jako argument nepoužíval - jsou to externí, neobjektové systémy bez principu zapouzdření, tudíž je třeba počítat s tím, že jestliže jsou jim svěřeny vnitřní stavy objektů, bude možno se k nim dostat. To je ale logické.
(Jak už se tu zmiňovalo) naopak přístup k vnitřním stavům je nežádoucí, protože ohrožuje konzistenci dat. Zapouzdření nevzniklo z pr_de_le!
No ale proc se v OOP programech tak casto pouzivaji relacni, a nikoli objektove, databaze? Neni to proto, ze je to fakticky jednodussi (jak z hlediska databaze tak z hlediska programatora)?
V zasade pokud si odpovite na tuhle otazku, dostanete i (castecnou) odpoved, proc je FP lepsi nez OOP.
Relačné DB sú v prvom rade o zvyku a okrem Caché neviem o žiadnej používanej objektovej DB. Napr. v staršom článku z cz/sk webu bol odkaz na objektovú DB smerujúci na adresu http://www.poet.com/ ...
Možno aplikačný server, alebo jeho admina lahšie odrbeš, že to nie je strata výkonu. Ale databázovy nie. Škálovať aplikačný server je ľahšie, ako škálovať databázový. Aspoň doteraz to tak bolo. Keď data rozdelíš po servroch, niečo/niekto bude musieť rozhodnúť, ktorá časť dat/objektov bude na ktorom servri. V relačnej DB sme už zvyknutý, ako to robiť.
Osobne predpokladám, že z hľadiska "objektového" programátora sú lepšie objektové databázy. Ale z hľadiska HW relačné. Ak by bola RAM DB servra nekonečná, alebo aspoň väčšia, ako DB s ktorou pracujeme, tak je to jedno, aký prístup k datam zvolíme.
Jedno to nebude nikdy, protože přístup do paměti také něco stojí a to, že si člověk může do objektové databáze uložit jakkoliv složitý objekt, znamená, že třeba migrace dat může být dost problémová záležitost.
Jinak existuje jedna objektová databáze, která se docela používá, jmenuje se ZODB. Nad ní je postaveno Zope a nad ním Plone (a různé jeho pluginy). Vydolovat z takové databáze data nebo přejít na vyšší verzi je prý docela fuška.
OK, už mi došlo, že v niektorych implementáciách relačných dat vieš jednoznačne podľa typu tabuľky a dat v nej a polohy jednej informácie polohu druhej. V relačnom svete je to asi jasnejšie. A ďalej načítavanie dat do cache procesora z relačnej, versus z objektovej DB... Takže výtku beriem, ak myslíte toto.
"Relačné DB sú v prvom rade o zvyku a okrem Caché neviem o žiadnej používanej objektovej DB."
Nezapomeňte na AllegroCache. Ale ony se ty objektové databáze asi nedají moc dobře použít, pokud nejsou opravdu těsně provázané s jazykem zbytku aplikace (což AllegroCache je, a jinde jsem nic takového neviděl).
Jinak relační DB se asi používají hlavně proto, že jsou inherentně paralelní.
Vcera jsem knihu v Narodni technicke projel na rychoposuv... a neni to spatne cteni. Ale skutecne jenom zaklady.