Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: stud 31. 08. 2015, 14:43:30
-
Řekněme, že chci mít objekty čtverec, obdélník, kosočtverec a rovnoběžník. Je jasné, že s jednoduchou dědičností se daleko nedostanu. Proto obecný dotaz: existuje nějaké jiné paradigma, ve kterém lze vztahy mezi objekty vyjádřit lépe? Pokud ano, na jaký jazyk bych se měl podívat?
-
A co s nimi chces delat? Odpoved se nejspis bude lisit podle toho, zda je budes malovat nebo trebas pocitat obsahy...
-
Myslíte vícenásobnou dědičnost? Ta je běžnou součástí OOP…
-
Řekněme, že chci mít objekty čtverec, obdélník, kosočtverec a rovnoběžník. Je jasné, že s jednoduchou dědičností se daleko nedostanu. Proto obecný dotaz: existuje nějaké jiné paradigma, ve kterém lze vztahy mezi objekty vyjádřit lépe? Pokud ano, na jaký jazyk bych se měl podívat?
To je jednoduché: Všechny mají společného předka, kterého můžeme pojmenovat třeba Obrazec. Tento předek by měl být abstraktním.
-
Řekněme, že chci mít objekty čtverec, obdélník, kosočtverec a rovnoběžník. Je jasné, že s jednoduchou dědičností se daleko nedostanu. Proto obecný dotaz: existuje nějaké jiné paradigma, ve kterém lze vztahy mezi objekty vyjádřit lépe? Pokud ano, na jaký jazyk bych se měl podívat?
To je jednoduché: Všechny mají společného předka, kterého můžeme pojmenovat třeba Obrazec. Tento předek by měl být abstraktním.
A jak takhle bez kontextu vis, ze to ma byt predek nebo protokol/interface?
-
Bez znalosti požadovaných vztahů mezi objekty těžko nalezneme odpověď.
-
Ajajaj.
Takže 3
2
1
fčíl!
-
Alternativa je zamyslet se trebas nad alebraickymi datovymi typy. Ale na to neni "spravna" odpoved bez dalsich informaci (a dost mozna i s nimi).
-
To je jednoduché: Všechny mají společného předka, kterého můžeme pojmenovat třeba Obrazec. Tento předek by měl být abstraktním.
A jak takhle bez kontextu vis, ze to ma byt predek nebo protokol/interface?
To je opět jednoduché: Odpověděl jsem v kontextu dotazu. Tazatel chtěl dědičnost, poskytl jsem mu společného předka. Pokud tazatel zkoriguje dotaz, mohu zkorigovat odpověď - stejně jako v TDD.
-
Short answer: Protocol-oriented programming: http://babel.blog.root.cz/2015/08/25/post-oop/ (http://babel.blog.root.cz/2015/08/25/post-oop/)
Long answer: Koncepční vztahy se deklarují pomocí protokolů. Např. každý tenzor je matice (ale ne naopak), každý vektor je tenzor a každý skalár je taky tenzor - jenže skalár je normální double (nebo komplexní číslo) a to už každý jazyk má a nedá se jim "podstrčit" nějaká nadtřída. Dá se ale říct, že vyhovují nějakému protokolu (a dodefinovat příslušné metody, např. pro derivaci tenzorových polí). Druhou výhodou je, že pak lze použít hodnotové typy, které z principu netvoří hierarchie (na rozdíl od tříd), ale zase program zrychlují. Sečteno a podtrženo: dědičnosti je lepší se úplně vyhnout.
-
Bez znalosti požadovaných vztahů mezi objekty těžko nalezneme odpověď.
Jaké vztahy na tom chceš hledat? Chceš mi snad tvrdit, že čtverec je obdélník, lichoběžník nebo kosočtverec? Nebo že čtverec má obdélník, lichoběžník nebo kosočtverec? Nic z toho není pravda, nemůžeš tedy použít ani dědičnost, ani kompozici.
Jsou to samostatné třídy, které mohou mít společného předka (např. Obrazec) nebo chceš-li rozhraní (např. Zobrazitelný).
-
Bez znalosti požadovaných vztahů mezi objekty těžko nalezneme odpověď.
Jaké vztahy na tom chceš hledat? Chceš mi snad tvrdit, že čtverec je obdélník, lichoběžník nebo kosočtverec? Nebo že čtverec má obdélník, lichoběžník nebo kosočtverec? Nic z toho není pravda, nemůžeš tedy použít ani dědičnost, ani kompozici.
Jsou to samostatné třídy, které mohou mít společného předka (např. Obrazec) nebo chceš-li rozhraní (např. Zobrazitelný).
Každý čtverec je obdélník.
-
Short answer: Protocol-oriented programming: http://babel.blog.root.cz/2015/08/25/post-oop/ (http://babel.blog.root.cz/2015/08/25/post-oop/)
Long answer: Koncepční vztahy se deklarují pomocí protokolů. Např. každý tenzor je matice (ale ne naopak), každý vektor je tenzor a každý skalár je taky tenzor - jenže skalár je normální double (nebo komplexní číslo) a to už každý jazyk má a nedá se jim "podstrčit" nějaká nadtřída. Dá se ale říct, že vyhovují nějakému protokolu (a dodefinovat příslušné metody, např. pro derivaci tenzorových polí). Druhou výhodou je, že pak lze použít hodnotové typy, které z principu netvoří hierarchie (na rozdíl od tříd), ale zase program zrychlují. Sečteno a podtrženo: dědičnosti je lepší se úplně vyhnout.
Perfekní rozbor, ale bohužel chybný závěr. Hodnotové typy do OOP nepatří. Jsou tam jen proto, že je programátoři chtěli.
-
Každý čtverec je obdélník.
Není - on tak jen vypadá. Čtverec má jeden atribut, obdélník má dva. 1 != 2.
-
stvorec extends obdlznik je standardny fail lebo liskov substitutuion principle
-
Short answer: Protocol-oriented programming: http://babel.blog.root.cz/2015/08/25/post-oop/ (http://babel.blog.root.cz/2015/08/25/post-oop/)
Long answer: Koncepční vztahy se deklarují pomocí protokolů. Např. každý tenzor je matice (ale ne naopak), každý vektor je tenzor a každý skalár je taky tenzor - jenže skalár je normální double (nebo komplexní číslo) a to už každý jazyk má a nedá se jim "podstrčit" nějaká nadtřída. Dá se ale říct, že vyhovují nějakému protokolu (a dodefinovat příslušné metody, např. pro derivaci tenzorových polí). Druhou výhodou je, že pak lze použít hodnotové typy, které z principu netvoří hierarchie (na rozdíl od tříd), ale zase program zrychlují. Sečteno a podtrženo: dědičnosti je lepší se úplně vyhnout.
Perfekní rozbor, ale bohužel chybný závěr. Hodnotové typy do OOP nepatří. Jsou tam jen proto, že je programátoři chtěli.
Protocol-oriented programming není OOP. Problém je, že žádný jazyk ho plně nepodporuje. Hodnotové typy jsou ortogonální koncept umožňující rychlejší a spolehlivější správu paměti (jako v C++, kde ovšem každý může rozhodnout pro každý objekt, chce-li ho mít na zásobníku).
-
Jaké vztahy na tom chceš hledat? Chceš mi snad tvrdit, že čtverec je obdélník, lichoběžník nebo kosočtverec? Nebo že čtverec má obdélník, lichoběžník nebo kosočtverec? Nic z toho není pravda, nemůžeš tedy použít ani dědičnost, ani kompozici.
Potřebujeme vědět co tazatel chce dělat.
O tom co mohu použít naštěstí nerozhoduješ ty, to by byla škoda, také netuším ze kterého klobouku a proč do toho taháš pojmy jako je a má :) Na tak triviální věc v 2D stačí společný předek ÜberQuadrat s členskými proměnnými bod, 2x délka, 2x úhel, rotace. Řešení je více, více variant, i pro 3D.
-
Každý čtverec je obdélník.
Není - on tak jen vypadá. Čtverec má jeden atribut, obdélník má dva. 1 != 2.
Jasně, že je. Počet atributů s tím nemá co dělat. https://cs.wikipedia.org/wiki/Čtyřúheln%C3%ADk (https://cs.wikipedia.org/wiki/Čtyřúheln%C3%ADk)
-
stvorec extends obdlznik je standardny fail lebo liskov substitutuion principle
liskova obecne zamita jenom obracenou variantu. Tahle varianta muze a nemusi byt v pohode. Pokud delas imutable objekty, tak v pohode (Z hlediska liskove) je.
Obdelnik(a,b)
a
Ctverec(a)=Obdelnik(a,a)
je z tohohle uhlu pohledu v OK. (Neni to uplne stastny napad trebas protoze ukladas jednu vec dvakrat a neumis moc postihnout nejake invarianty, ale to s liskovou nesouvisi)
-
Každý čtverec je obdélník.
Není - on tak jen vypadá. Čtverec má jeden atribut, obdélník má dva. 1 != 2.
V reálném světě ovšem čtverec je speciální případ obdélníku, který má všechny čtyři strany stejně dlouhé. To, že běžné OOP jazyky mají atributy a umožňují je při dědění pouze přidávat, je jejich omezení, a teprve toto omezení vede na to, že se hierarchie udělá opačně, tj. obdélník je rozšíření čtverce. Navíc pokud použijete správně zapouzdření, nebudou vám vadit ani ty atributy, protože čtverec naimplementujete tak, že nastavením jednoho rozměru změníte i druhý, a zbytek už bude fungovat jako speciální případ obdélníku.
-
Každý čtverec je obdélník.
Není - on tak jen vypadá. Čtverec má jeden atribut, obdélník má dva. 1 != 2.
Doporučuji zopakovat základy geometrie. (http://mathcentral.uregina.ca/QQ/database/QQ.09.07/h/odette1.html)
stvorec extends obdlznik je standardny fail lebo liskov substitutuion principle
V LSP záleží na tom, jaké rozhraní třídy mají (nejen typy, ale i jak je definována činnost metod). Tj. LSP nijak nebrání například napsat, že Pes je podtyp Kniha, když rozhraní budou prázdná a objekty nebudou nic dělat.
-
... také netuším ze kterého klobouku a proč do toho taháš pojmy jako je a má ...
Tato dvě pomocná slovesa jsou významnou a přitom jednoduchou pomůckou při rozhodování, zda použít dědičnost či kompozici. Ještě mě nezklamala.
-
Přijde mi že skoro všechny matematické objekty "dědí" způsobem, že se přidávají nějaké podmínky a ubývají stupně volnosti. Naproti tomu běžné dědění umí stupně volnosti spíš přidávat a přidávání podmínek jde taky dost blbě. Minimálně pokud ten objekt není invariantní. I u reálných objektů mi přijde, že vztah je spíš přidává další a další omezení.
Na tohle mi daleko víc pasují classy v Haskellu. Čtverec je immutable, takže se nedá rozbít i když se s ním pracuje jako s obdélníkem. Classy nemají žádná data ale jenom "metody" respektive funkce. V podstatě je to dědičnost omezená na interfacy.
Jak nad tím tak přemýšlím, tak v C++ už dlouho používám public dědičnost jen pro abstraktní třídy (interfacy) a privátní dědičnost jako implementační hack.
-
Tato dvě pomocná slovesa jsou významnou a přitom jednoduchou pomůckou při rozhodování, zda použít dědičnost či kompozici. Ještě mě nezklamala.
No tak právě teď tento postup selhal :) Čtverec, Obdélník, Kosočtverec i Lichoběžník jsou všichni potomci ÜberQuadrat :)
-
... také netuším ze kterého klobouku a proč do toho taháš pojmy jako je a má ...
Tato dvě pomocná slovesa jsou významnou a přitom jednoduchou pomůckou při rozhodování, zda použít dědičnost či kompozici. Ještě mě nezklamala.
To je obecně špatný přístup. Dědičnost totiž není o podtypech, ale o opakovaném využití kódu. Klasický příklad, kdy děděním nevznikají podtypy, jsou tzv. binární metody.
Při rozhodování, zda je něco podtyp, je lepší použít plný LSP:
If for each object o1 of type S there is another object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.
Mj. je vidět, že například reflexe může zneplatnit LSP - typy T a S lze rozlišit reflexí.
-
Každý čtverec je obdélník.
Není - on tak jen vypadá. Čtverec má jeden atribut, obdélník má dva. 1 != 2.
V reálném světě ovšem čtverec je speciální případ obdélníku, který má všechny čtyři strany stejně dlouhé. To, že běžné OOP jazyky mají atributy a umožňují je při dědění pouze přidávat, je jejich omezení, a teprve toto omezení vede na to, že se hierarchie udělá opačně, tj. obdélník je rozšíření čtverce. Navíc pokud použijete správně zapouzdření, nebudou vám vadit ani ty atributy, protože čtverec naimplementujete tak, že nastavením jednoho rozměru změníte i druhý, a zbytek už bude fungovat jako speciální případ obdélníku.
Hezké ... ale k čemu je to dobré? Na jednu stranu čtu, že se někdo vyhýbá dědičnosti jako čert kříži, z druhé strany mi zase někdo vnucuje čtverec jako potomka obdélníku. Nevidím důvod, proč bych měl zavádět dědičnost mezi třídami, u kterých jsou pochybnosti o paternitě a dokonce ani není problém vytvořit nového (abstraktního) předka či rozhraní pro všechny zmíněné třídy.
-
V reálném světě ovšem čtverec je speciální případ obdélníku, který má všechny čtyři strany stejně dlouhé. To, že běžné OOP jazyky mají atributy a umožňují je při dědění pouze přidávat, je jejich omezení, a teprve toto omezení vede na to, že se hierarchie udělá opačně, tj. obdélník je rozšíření čtverce.
Ale fuj. Zrovna u toho čtverce a obdélníka bych se raději bez dědění obešel, než tohle. Co má takový rozšířený čtverec vracet, když se ho zeptám na stranu? Jakákoliv jen trochu příčetná volba rozbije invarianty třeba u obsahu.
Navíc pokud použijete správně zapouzdření, nebudou vám vadit ani ty atributy, protože čtverec naimplementujete tak, že nastavením jednoho rozměru změníte i druhý, a zbytek už bude fungovat jako speciální případ obdélníku.
Ještě lepší. V tomhle případě nemůžu přidávat žádné třídy dodatečně. Buď bude z rozhraní obdélníka jasné, že za ním může být schovaný i čtverec, nebo z toho bude ohavná past.
rect->setSize(s);
assert(rect->size() == s); // trololololo
-
... také netuším ze kterého klobouku a proč do toho taháš pojmy jako je a má ...
Tato dvě pomocná slovesa jsou významnou a přitom jednoduchou pomůckou při rozhodování, zda použít dědičnost či kompozici. Ještě mě nezklamala.
To je obecně špatný přístup. Dědičnost totiž není o podtypech, ale o opakovaném využití kódu. Klasický příklad, kdy děděním nevznikají podtypy, jsou tzv. binární metody.
Mýlíš se. Implementace musí být závislá na abstrakci - nikdy naopak. Opakované využití kódu z toho vyplyne zcela automaticky.
Při rozhodování, zda je něco podtyp, je lepší použít plný LSP:
V tom máš sice pravdu, ale zkus to tak vysvětlit začátečníkům. Budou na tebe civět s otevřenou hubou. Slovesa je a má pochopí mnohem rychleji, přesnou definici je můžeš naučit později.
-
No vida!
A pak bych to ještě vyšperkoval několika variacemi na téma getter a setter (ne ten pes) ...
-
No vida!
A pak bych to ještě vyšperkoval několika variacemi na téma getter a setter (ne ten pes) ...
Ja na ten copy-paste z ucebnice OOP for Dummies cekam uz minimalne hodinu
-
Při rozhodování, zda je něco podtyp, je lepší použít plný LSP:
If for each object o1 of type S there is another object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.
Mj. je vidět, že například reflexe může zneplatnit LSP - typy T a S lze rozlišit reflexí.
Jak to čtu znovu, tak vidím jediné: Duck typing. To není zrovna nejlepší ukázkou definice dědičnosti. Možná, kdybys to nevytrhl z kontextu...
-
Hezké ... ale k čemu je to dobré? Na jednu stranu čtu, že se někdo vyhýbá dědičnosti jako čert kříži, z druhé strany mi zase někdo vnucuje čtverec jako potomka obdélníku. Nevidím důvod, proč bych měl zavádět dědičnost mezi třídami, u kterých jsou pochybnosti o paternitě a dokonce ani není problém vytvořit nového (abstraktního) předka či rozhraní pro všechny zmíněné třídy.
Třeba proto, že všechno, co platí pro obdélník, platí i pro čtverec? Když budu chtít spočítat úhlopříčku nebo obsah, proč bych to měl pro čtverec implementovat znova? Proč má uživatel mého kódu tam, kde chci na vstupu obdélník, vytvářet nový obdélník podle zadaného čtverce, místo toho, aby využil toho, že čtverec je zároveň i obdélník?
V tomhle případě nemůžu přidávat žádné třídy dodatečně. Buď bude z rozhraní obdélníka jasné, že za ním může být schovaný i čtverec, nebo z toho bude ohavná past.
Co je špatného na tom, když ten obdélník bude mít metody setStranaA() a setStranaB()? Jak se z toho pozná, že za tím může být schovaný i čtverec? Je snad něco, co můžete udělat s obdélníkem, ale ne se čtvercem? Čtverec je speciální případ obdélníku, takže všude tam, kde je očekáván obdélník, musí být možné předat i čtverec.
-
No vida!
A pak bych to ještě vyšperkoval několika variacemi na téma getter a setter (ne ten pes) ...
Ja na ten copy-paste z ucebnice OOP for Dummies cekam uz minimalne hodinu
V té učebnici "OOP for Dummies" používají gettery a settery? Tak už je mi jasné, proč to všichni tak propagují...
-
No vida!
A pak bych to ještě vyšperkoval několika variacemi na téma getter a setter (ne ten pes) ...
Ja na ten copy-paste z ucebnice OOP for Dummies cekam uz minimalne hodinu
V té učebnici "OOP for Dummies" používají gettery a settery? Tak už je mi jasné, proč to všichni tak propagují...
Skoro, Kite, skoro...
-
Nevidím důvod, proč bych měl zavádět dědičnost mezi třídami, u kterých jsou pochybnosti o paternitě a dokonce ani není problém vytvořit nového (abstraktního) předka či rozhraní pro všechny zmíněné třídy.
Takhle se to dělá furt. Máme FileStream a protože se nám líbí práce s ním, tak se udělá obecný abstraktní předek IOStream a pak k němu pět dalších potomků.
-
... také netuším ze kterého klobouku a proč do toho taháš pojmy jako je a má ...
Tato dvě pomocná slovesa jsou významnou a přitom jednoduchou pomůckou při rozhodování, zda použít dědičnost či kompozici. Ještě mě nezklamala.
To je obecně špatný přístup. Dědičnost totiž není o podtypech, ale o opakovaném využití kódu. Klasický příklad, kdy děděním nevznikají podtypy, jsou tzv. binární metody.
Mýlíš se. Implementace musí být závislá na abstrakci - nikdy naopak. Opakované využití kódu z toho vyplyne zcela automaticky.
Nerozumím, proč bych se měl mýlit. Podstatou mého tvrzení je, že použitím dědičnosti nemusí vzniknout podtyp - příkladem je třeba metoda equals, když ji otypuji equals : Self -> bool. Ve tříde Object je typ ekvivalentní equals : Object -> bool a po zdědění třídou Pes je typ ekvivalentní equals : Pes -> bool - za Self se vždy dosadí typ aktuální třídy. Je vidět, že Pes není podtyp Object, neboť argumenty metod jsou kontravariantní. Podrobněji viz článek On Binary Methods (http://lucacardelli.name/Papers/Binary.pdf).
Při rozhodování, zda je něco podtyp, je lepší použít plný LSP:
If for each object o1 of type S there is another object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.
Mj. je vidět, že například reflexe může zneplatnit LSP - typy T a S lze rozlišit reflexí.
Jak to čtu znovu, tak vidím jediné: Duck typing. To není zrovna nejlepší ukázkou definice dědičnosti. Možná, kdybys to nevytrhl z kontextu...
Tady jde i o chování, nejen o signatury metod.
Definice je z článku Data Abstraction and Hierarchy (http://www.rendezvouswithdestiny.net/index_files/LiskovSub.pdf) od Barbary Liskov z roku 1988, sekce 3.3. Alternativní definice LSP je v článku A Behavioral Notion of Subtyping (http://www.cs.cmu.edu/~wing/publications/LiskovWing94.pdf) od B. Liskov and J. M. Winga z roku 1994 v části úvod.
-
Při rozhodování, zda je něco podtyp, je lepší použít plný LSP:
If for each object o1 of type S there is another object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.
Mj. je vidět, že například reflexe může zneplatnit LSP - typy T a S lze rozlišit reflexí.
Jak to čtu znovu, tak vidím jediné: Duck typing. To není zrovna nejlepší ukázkou definice dědičnosti. Možná, kdybys to nevytrhl z kontextu...
Jinak ještě jedna poznámka: LSP nemá s dědičností nic společného - podtřída a podtyp jsou různé věci - v některých jazycích můžete mít podtřídu aniž by to byl podtyp (viz binární metody) a také podtyp aniž by to byla podtřída.
-
Jak to čtu znovu, tak vidím jediné: Duck typing. To není zrovna nejlepší ukázkou definice dědičnosti. Možná, kdybys to nevytrhl z kontextu...
Tady jde i o chování, nejen o signatury metod.
Právě Duck typing je definován tím chováním, signatury jsou vedlejší. Je to alternativní definice dědičnosti nikoli primární.
Je to můj syn, ale nechová se jako já × není to můj syn, ale chová se jako já.
To jsou dva typy dědičnosti. Pokud modifikuji původní sloveso je na chová se, dostanu definici Duck typing dle Barbary Liskov.
-
Co je špatného na tom, když ten obdélník bude mít metody setStranaA() a setStranaB()? Jak se z toho pozná, že za tím může být schovaný i čtverec? Je snad něco, co můžete udělat s obdélníkem, ale ne se čtvercem? Čtverec je speciální případ obdélníku, takže všude tam, kde je očekáván obdélník, musí být možné předat i čtverec.
Pozná se to z dokumentace těch metod. Může setStranaA ovlivnit i B nebo ne? Co můžu o tom obdélníku říct po jejich zavolání? Pokud to v dokumentaci nebude přímo napsané, pak bych u obdélníka očekával strany které jsou na sobě nezávislé. Ten čtverec tam nemůžu přidat dodatečně.
-
A vidite, kolik problemu je se stavem? ;)
-
A vidite, kolik problemu je se stavem? ;)
Souhlas. Ve chvíli kdy nemůžu měnit stav se to krásně pročistí :) V té chvíli můžu interface čtverce dědit z interfacu obdélníka a všechno se chová příčetně.
-
Jak to čtu znovu, tak vidím jediné: Duck typing. To není zrovna nejlepší ukázkou definice dědičnosti. Možná, kdybys to nevytrhl z kontextu...
Tady jde i o chování, nejen o signatury metod.
Právě Duck typing je definován tím chováním, signatury jsou vedlejší. Je to alternativní definice dědičnosti nikoli primární.
Tady ale nejsou signatury vedlejší.
To jsou dva typy dědičnosti.
Nepřijde mi, že by to mělo něco společného s dědičností. Jak jsem již říkal, můžete používat dědičnost aniž by vznikl podtyp - instance podtříd nepůjde použít tam, kde jsou očekávány instance nadtříd (kompilátor to nedovolí, protože to nedává smysl).
-
A vidite, kolik problemu je se stavem? ;)
Souhlas. Ve chvíli kdy nemůžu měnit stav se to krásně pročistí :) V té chvíli můžu interface čtverce dědit z interfacu obdélníka a všechno se chová příčetně.
Dalšími omezeními jde předejít dalším problémům. Pro C++ existují například BPravidla (http://okmij.org/ftp/Computation/Subtyping/Preventing-Trouble.html#BRules):
- no virtual methods or virtual inheritance
- no visible members or methods in any public data structure (that is, in any class declared in an .h file)
- no mutations to public data structures
- a strict form: no assignments or mutations whatsoever
- a less strict form: no function may alter, directly or indirectly, any data it receives as arguments
-
A vidite, kolik problemu je se stavem? ;)
Normálně by mělo být Square extends Rectangle a evtl. MutableRectangle extends Rectangle. Tím se oddělí "zrno od plev" (funkcionální část objektu od té měnitelné). Ovšem lepší je mít vše immutable.
-
Dalšími omezeními jde předejít dalším problémům. Pro C++ existují například BPravidla (http://okmij.org/ftp/Computation/Subtyping/Preventing-Trouble.html#BRules):
- no virtual methods or virtual inheritance
- no visible members or methods in any public data structure (that is, in any class declared in an .h file)
- no mutations to public data structures
- a strict form: no assignments or mutations whatsoever
- a less strict form: no function may alter, directly or indirectly, any data it receives as arguments
Tak tohle mi už přijde jako úlet. Zakáže polovinu C++ a pak na to ručně bez podpory překladače roubuje Haskell. C++ už je dostatečný bordel i tak. Pokud chci letadlo, tak přece nebudu šroubovat křídla na traktor. :o
-
Dalšími omezeními jde předejít dalším problémům. Pro C++ existují například BPravidla (http://okmij.org/ftp/Computation/Subtyping/Preventing-Trouble.html#BRules):
- no virtual methods or virtual inheritance
- no visible members or methods in any public data structure (that is, in any class declared in an .h file)
- no mutations to public data structures
- a strict form: no assignments or mutations whatsoever
- a less strict form: no function may alter, directly or indirectly, any data it receives as arguments
Ještě bych prosil zakázat gettery a settery ... :)
-
Dalšími omezeními jde předejít dalším problémům. Pro C++ existují například BPravidla (http://okmij.org/ftp/Computation/Subtyping/Preventing-Trouble.html#BRules):
- no virtual methods or virtual inheritance
- no visible members or methods in any public data structure (that is, in any class declared in an .h file)
- no mutations to public data structures
- a strict form: no assignments or mutations whatsoever
- a less strict form: no function may alter, directly or indirectly, any data it receives as arguments
Tak tohle mi už přijde jako úlet. Zakáže polovinu C++ a pak na to ručně bez podpory překladače roubuje Haskell. C++ už je dostatečný bordel i tak. Pokud chci letadlo, tak přece nebudu šroubovat křídla na traktor. :o
Souhlasím. Ostatně i autor to poznamenává v závěru:
BRules are at odds with the practice if not the very mentality of OOP. This begs the question: Is OOP indeed conducive to software development?
Nicméně BPravidla údajně garantují (pro C++ 98), že podtřída je podtyp (zřejmě se nepředpokládá použití reflexe) - tj. máte rozhodnutelná pravidla, jenž implikují platnost LSP.
-
Nicméně BPravidla údajně garantují (pro C++ 98), že podtřída je podtyp (zřejmě se nepředpokládá použití reflexe) - tj. máte rozhodnutelná pravidla, jenž implikují platnost LSP.
Nemám nějaký formální důkaz ale na tohle by snad stačilo vynutit immutable data podle třetího bodu. Zakázal virtuální metody, ale pak si je tam stejně implementuje ručně (příklad s tvary). Zakázal členské metody ale pak je tam stejně implementuje ručně akorát bez tečky.
-
Nicméně BPravidla údajně garantují (pro C++ 98), že podtřída je podtyp (zřejmě se nepředpokládá použití reflexe) - tj. máte rozhodnutelná pravidla, jenž implikují platnost LSP.
Nemám nějaký formální důkaz ale na tohle by snad stačilo vynutit immutable data podle třetího bodu.
To bohužel nestačí. Můžete vzít například třídu T s jednou virtuální metodou, jejíž chování je definováno tak, že vždy vrátí konstantu 1. Když pak vezmete podtřídu S, kde tato metoda vrací konstantu 2, tak to už není podtyp. O objektech typu T snadno dokážete, že daná metoda vrátí 1. Tuto vlastno nejde dokázat pro objekty typu S, tj. podle LSP S není podtyp T (podle definice LSP z A Behavioral Notion of Subtyping (http://www.cs.cmu.edu/~wing/publications/LiskovWing94.pdf), hned v úvodu na druhé stránce - Subtype Requirement).
Obecně rozhodovat LSP je ostře těžší než rozhodovat HP.
-
Čtverec ani obdélník jako objekt nepotřebujete, stačí kosodélník definovaný strana a, strana b, úhel alfa, obdélník je Kosodélník(a, b, 90), čtverec je Kosodélník(a, a, 90), výpočet obsahu, obvodu je univerzální. V programu není důvod využívat speciálních vlastností obdélníka, nebo čtverce.
-
Čtverec ani obdélník jako objekt nepotřebujete, stačí kosodélník definovaný strana a, strana b, úhel alfa, obdélník je Kosodélník(a, b, 90), čtverec je Kosodélník(a, a, 90), výpočet obsahu, obvodu je univerzální.
S touto logikou nepotřebuju ani kosodelník, vystačím si na všechno s polygonem.
V programu není důvod využívat speciálních vlastností obdélníka, nebo čtverce.
Samozřejmě že je důvod, např. ve hrách se kolize objektů řeší nejprve na úrovni axis aligned bounding boxu objektu (obdelník pro 2D, kvádr pro 3D), je to mnohem efektivnější než to počítat pro obecné polygony.
-
To bohužel nestačí. Můžete vzít například třídu T s jednou virtuální metodou, jejíž chování je definováno tak, že vždy vrátí konstantu 1. Když pak vezmete podtřídu S, kde tato metoda vrací konstantu 2, tak to už není podtyp. O objektech typu T snadno dokážete, že daná metoda vrátí 1. Tuto vlastno nejde dokázat pro objekty typu S, tj. podle LSP S není podtyp T (podle definice LSP z A Behavioral Notion of Subtyping (http://www.cs.cmu.edu/~wing/publications/LiskovWing94.pdf), hned v úvodu na druhé stránce - Subtype Requirement).
Pravda, tohle není podtyp. Tomuhle ale nezabrání ani ty BPravidla. Ve Shapes-no-oop.cc implementuje vlastní virtuální funkce, které ty pravidla neporušují a přitom s nima jde tohle udělat taky. Zakázal sice klíčové slovo virtual ale implementuje tam vlastní VFT pomocí ukazatelů na funkce.
Vlastně si nedokážu představit, jakým způsobem by se ty podtypy daly zaručit. Na to aby to nebyl podtyp stačí jakýkoliv bug v implementaci vlastností.
-
opět se potřebuju vyznat k obdivu k Haskellu
definoval bych si datové typy, které přesně popisuje danou entitu (data Čtverec = Čtverec Integer etc.) a pak bych si definoval operace společné pro různé obrazce pomoc typových tříd (type class), jenom data a výpočty, žádné zbytečné abstrakce
-
nebo sum type :)
-
To bohužel nestačí. Můžete vzít například třídu T s jednou virtuální metodou, jejíž chování je definováno tak, že vždy vrátí konstantu 1. Když pak vezmete podtřídu S, kde tato metoda vrací konstantu 2, tak to už není podtyp. O objektech typu T snadno dokážete, že daná metoda vrátí 1. Tuto vlastno nejde dokázat pro objekty typu S, tj. podle LSP S není podtyp T (podle definice LSP z A Behavioral Notion of Subtyping (http://www.cs.cmu.edu/~wing/publications/LiskovWing94.pdf), hned v úvodu na druhé stránce - Subtype Requirement).
Pravda, tohle není podtyp. Tomuhle ale nezabrání ani ty BPravidla. Ve Shapes-no-oop.cc implementuje vlastní virtuální funkce, které ty pravidla neporušují a přitom s nima jde tohle udělat taky.
AFAIK tohle, co jsem napsal, se s BPravidly udělat nedá. Důvod je, že původně virtuální metoda z T, ale s BPravidly funkce mimo T nevrací vždy 1 - její činnost je totiž definována funkcí, která se předá konstruktoru T (podobně jako u odkazovaného Shape, kde je činnost metod definována funkcemi z konstruktoru). Jinak řečeno pro každou instanci S můžete vyrobit instanci T, která se chová stejně (funkce co konstruktor S předával konstruktoru T přímo předáte konstruktoru T, čímž získáte požadovanou instanci T).
-
AFAIK tohle, co jsem napsal, se s BPravidly udělat nedá. Důvod je, že původně virtuální metoda z T, ale s BPravidly funkce mimo T nevrací vždy 1 - její činnost je totiž definována funkcí, která se předá konstruktoru T (podobně jako u odkazovaného Shape, kde je činnost metod definována funkcemi z konstruktoru). Jinak řečeno pro každou instanci S můžete vyrobit instanci T, která se chová stejně (funkce co konstruktor S předával konstruktoru T přímo předáte konstruktoru T, čímž získáte požadovanou instanci T).
Bojím se že nechápu. Vidím tam jen dva rozdíly. Jestli je VFT uložená přímo v objektu nebo nepřímo přes ukazatel. A druhý je jestli ty ukazatele inicializuju v konstruktoru já nebo překladač. Jestli tu funkci zadrátuju do konstruktoru já nebo překladač vidím jenom kosmetický rozdíl. V tom, co si do těch funkcí nacpu, mě ani v jednom případě překladač nijak neomezuje.
V tom odkazovaném případě je ekvivalent jedné abstraktní třídy a jedné vrstvy odvozených. Nenapadá mě ale jak by mi BP zabránily udělat několik vrstev odvozených typů a např. nějak divoce ifovat v konstruktorech. V obou případech dokážu ty virtuální funkce inicializovat podle libosti a ani jedno pravidlo mi v tom nebrání. I ty tovární metody "make" můžou dělat skoro cokoliv.
-
AFAIK tohle, co jsem napsal, se s BPravidly udělat nedá. Důvod je, že původně virtuální metoda z T, ale s BPravidly funkce mimo T nevrací vždy 1 - její činnost je totiž definována funkcí, která se předá konstruktoru T (podobně jako u odkazovaného Shape, kde je činnost metod definována funkcemi z konstruktoru). Jinak řečeno pro každou instanci S můžete vyrobit instanci T, která se chová stejně (funkce co konstruktor S předával konstruktoru T přímo předáte konstruktoru T, čímž získáte požadovanou instanci T).
Bojím se že nechápu. Vidím tam jen dva rozdíly. Jestli je VFT uložená přímo v objektu nebo nepřímo přes ukazatel. A druhý je jestli ty ukazatele inicializuju v konstruktoru já nebo překladač. Jestli tu funkci zadrátuju do konstruktoru já nebo překladač vidím jenom kosmetický rozdíl. V tom, co si do těch funkcí nacpu, mě ani v jednom případě překladač nijak neomezuje.
V tom odkazovaném případě je ekvivalent jedné abstraktní třídy a jedné vrstvy odvozených. Nenapadá mě ale jak by mi BP zabránily udělat několik vrstev odvozených typů a např. nějak divoce ifovat v konstruktorech. V obou případech dokážu ty virtuální funkce inicializovat podle libosti a ani jedno pravidlo mi v tom nebrání. I ty tovární metody "make" můžou dělat skoro cokoliv.
Bez BPravidel můžete v nadtřídě vytvořit virtuální metodu M s vlastností P. V podtřídě pak můžete tuto metodu M překrýt a to tak, že vlastnost P přestane platit. Pokud bylo možné vlastnost P odvodit ze specifikace nadtřídy, tak podtřída nemůže být podtyp, neboť P tam neplatí. Tj. máme podtřídu, která podle LSP není podtyp.
Naopak s BPravidly je činnost M (nyní funkce mimo třídu) definována funkcí, která se předá konstruktoru nadtřídy. Díky tomu podtřída nikdy nemůže dosáhnout chování M, které by už neuměla nadtřída - M v podtřídě je tedy speciální případ M v nadtřídě.
U toho mého původního příkladu M v nadtřídě vracela vždy 1 a M v podtřídě vždy 2. S BPravidly však podtřída může M ovlivnit pouze pomocí konstruktoru nadtřídy - M v nadtřídě umí nasimulovat každé chování M v podtřídě (stačí do konstruktoru nadtřídy předat totéž, co tam předala podtřída). V našem případě podtřída předala funkci vracející 2 - tuto funkci můžeme předat přímo při konstrukci nadtřídy, čímž se ukáže, že není pravda, že M v nadtřídě vrací vždy 1 (tj. ukáže se, že vlastnost P neplatí ani v nadtřídě).
Autor BPravidel to ukazuje na příkladu s FSet <: FBag:
A set is fully a bag. Because FSet constructors eventually call FBag constructors and do no alter the latter's result, every post-condition of a FSet constructor implies a post-condition of a FBag constructor. Since FBag and FSet values are immutable, the post-conditions that hold at their birth remain true through their lifespan. Because all FSet values are created by an FBag constructor, all FBag operations automatically apply to an FSet value. This concludes the proof that an FSet is a subtype of a FBag.
-
Tak té nadtřídě při konstrukci podtřídy šoupnu ukazatel na funkci, která nesplňuje požadavky.
Zkusím ilustrovat hodně syntetickým příkladem (podle Shapes-no-oop) :
class Nad
{
int (*const get)();
protected :
Nad(int (*const g)()) : get(g) {}
friend int get_odd(Nad const &);
};
// vzdycky vraci liche cislo
int get_odd(Nad const & n)
{
return n.get();
}
class Huj: public Nad
{
static int get_ok()
{
return 1;
}
protected :
Huj() : Nad(get_ok) {}
public :
static const Huj* make()
{
return new Huj();
}
};
class Fuj: public Nad
{
static int get_ko()
{
return 2;
}
protected :
Fuj() : Nad(get_ko) {}
public :
static const Fuj* make()
{
return new Fuj();
}
};
Je tam porušené nějaké BPravidlo?
Kam tím směřuju? Ta lichá čísla jsou součástí specifikace nadtřídy a ona mi nedokáže zabránit tu specifikaci porušit. Pokud píšu cokoliv užitečného, tak budu mít část té specifikace někde v komentáři nad rámec jazyka. Tady by se to dalo ošetřit, nebo aspoň zkontrolovat assertem, ale tohle není možné udělat vždycky.
Jo, čistě teoreticky ta nadtřída umí vždycky to, co podtřída. Akorát to znamená jediné. Nejkonkrétnější specifikace o které si u jakékoliv nadtřídy můžu nechat zdát je něco jako nedefinované chování. To můžu udělat i pomocí
#define mysterious virtual
a nemusím si VFT psát ručně. U těch BPravidel prostě vidím přínos jen v tom třetím. První dvě mě jen nutí napsat si naprosto ekvivalentní konstrukce bez podpory jazyka.
Co mi přinese totální zákaz public metod v porovnání se situací kdy mám díky třetímu pravidlu povolené jen const metody?
-
Všichni, kdož jste na takto položený dotaz odpověděli jinak, než že na něj nelze dát uspokojivou odpověď, byste se měli vys..t na programování a raději se věnovat něčemu jinému.
Jinak bude vypadat objektový návrh v C++, jinak v Javě, jinak v Pythonu, jinak ve Smalltalku, jinak pro účely grafické, jinak pro účely matematické, jinak pro účely čmárání po obrazovce myší, jinak pro účely vykreslení něčeho plottrem, jinak pro účely algebraické, jinak pro účely geometrické a docela jinak, pokud by se mělo všechno výše uvedené nějak skloubit dohromady. A pak přijde někdo s požadavkem doplnění sférické geometrie a další bude chtít diferenciální a uvidíte, jak jste v ...
Každý návrh se odvíjí od účelu. Nesmí být ani zbytečně abstraktní, ani zbytečně omezující. Odhadnutí správné míry je právě to, co dělá z relativně mechanické, kuchařkovité práce umění. Které ovšem 99% lidí v IT neovládá.
-
Kam tím směřuju? Ta lichá čísla jsou součástí specifikace nadtřídy a ona mi nedokáže zabránit tu specifikaci porušit.
Rozumím. Já jsem předpokládal, že ta specifikace nadtřídy je korektní.
Je možná tedy lepší používat tu první definici LSP (tu používá i autor BPravidel):
If for each object o1 of type S there is another object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.
Já ji nicméně nemám tak rád, neboť tato definice pracuje se všemi chováními - nejen těmi ve specifikaci - což je někdy zbytečně omezující. Nicméně se nemusí řešit správnost specifikace a ani to, co znamená něco dokázat ze specifikace (v tom článku je to definováno - začátek strany 14 - We view each type specification as a theory presentation, i.e., a set of symbols, rules for forming well-formed formula..., ale pochybuji, že se tím v praxi bude někdo řídit).
-
Rozumím. Já jsem předpokládal, že ta specifikace nadtřídy je korektní.
Nejsem zrovna dobrý teoretik. Co přesně znamená že je specifikace korektní? Co je nekorektní na specifikaci, že nějaká metoda/funkce vrací vždy lichý výsledek?
Je možná tedy lepší používat tu první definici LSP (tu používá i autor BPravidel):
If for each object o1 of type S there is another object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.
Já ji nicméně nemám tak rád, neboť tato definice pracuje se všemi chováními - nejen těmi ve specifikaci - což je někdy zbytečně omezující. Nicméně se nemusí řešit správnost specifikace a ani to, co znamená něco dokázat ze specifikace (v tom článku je to definováno - začátek strany 14 - We view each type specification as a theory presentation, i.e., a set of symbols, rules for forming well-formed formula..., ale pochybuji, že se tím v praxi bude někdo řídit).
Podle téhle definice ale nejede při dokazování že Set je podtyp Bag. Pokud Set na rozdíl od Bag vyháže duplicity, tak změní výsledek hodně programů. Vlastně teď marně přemýslím nad Bagem který by nebyl jen kopie Setu, a u kterého by ty podmínky byly splněné.
-
Rozumím. Já jsem předpokládal, že ta specifikace nadtřídy je korektní.
Nejsem zrovna dobrý teoretik. Co přesně znamená že je specifikace korektní? Co je nekorektní na specifikaci, že nějaká metoda/funkce vrací vždy lichý výsledek?
Korektní jsem měl na mysli to, že je v souladu s tím programem.
Je možná tedy lepší používat tu první definici LSP (tu používá i autor BPravidel):
If for each object o1 of type S there is another object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.
Já ji nicméně nemám tak rád, neboť tato definice pracuje se všemi chováními - nejen těmi ve specifikaci - což je někdy zbytečně omezující. Nicméně se nemusí řešit správnost specifikace a ani to, co znamená něco dokázat ze specifikace (v tom článku je to definováno - začátek strany 14 - We view each type specification as a theory presentation, i.e., a set of symbols, rules for forming well-formed formula..., ale pochybuji, že se tím v praxi bude někdo řídit).
Podle téhle definice ale nejede při dokazování že Set je podtyp Bag. Pokud Set na rozdíl od Bag vyháže duplicity, tak změní výsledek hodně programů. Vlastně teď marně přemýslím nad Bagem který by nebyl jen kopie Setu, a u kterého by ty podmínky byly splněné.
Jeho třída Set je podtyp té třídy Bag, jelikož i instance třídy Bag se mohou chovat tak, že budou vyhazovat duplicity (stačí jim předat vhodné funkce v konstruktoru).
-
stačí jim předat vhodné funkce v konstruktoru
Chyba (tohle bylo u tvarů) - tady se předává FBag bez duplikátů.
Můžeme to zkusit dle definice:
If for each object o1 of type S there is another object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.
Pro každý o1 typu FSet (bez duplikátů) existuje o2 typu FBag bez duplikátů (to je zřejmé). To, že se chování programů nezmění, když místo o1 použijeme o2 ukážeme například pro put: put vezme neměnný spojový seznam z o1 nebo o2 (je to jedno), přidá na jeho začátek daný prvek a vrátí nový FBag.
-
Řekněme, že chci mít objekty čtverec, obdélník, kosočtverec a rovnoběžník. Je jasné, že s jednoduchou dědičností se daleko nedostanu. Proto obecný dotaz: existuje nějaké jiné paradigma, ve kterém lze vztahy mezi objekty vyjádřit lépe? Pokud ano, na jaký jazyk bych se měl podívat?
Preco chces vlastne dedit? Nestacia ti 4 structury bez dedicnosti?
-
Všichni, kdož jste na takto položený dotaz odpověděli jinak, než že na něj nelze dát uspokojivou odpověď, byste se měli vys..t na programování a raději se věnovat něčemu jinému.
....
Každý návrh se odvíjí od účelu. Nesmí být ani zbytečně abstraktní, ani zbytečně omezující. Odhadnutí správné míry je právě to, co dělá z relativně mechanické, kuchařkovité práce umění. Které ovšem 99% lidí v IT neovládá.
Amen(doufam, ze to neni rouhani).
Konecne nekdo ...
-
Všichni, kdož jste na takto položený dotaz odpověděli jinak, než že na něj nelze dát uspokojivou odpověď, byste se měli vys..t na programování a raději se věnovat něčemu jinému.
....
Každý návrh se odvíjí od účelu. Nesmí být ani zbytečně abstraktní, ani zbytečně omezující. Odhadnutí správné míry je právě to, co dělá z relativně mechanické, kuchařkovité práce umění. Které ovšem 99% lidí v IT neovládá.
Amen(doufam, ze to neni rouhani).
Konecne nekdo ...
Jak konečně? To už bylo v první odpovědi (Ondřej Nekola). Kromě toho je Tebou adorovaný příspěvek vnitřně nekonzistentní - jestliže 99% programátotů jsou nemyslící lamy a měli by toho nechat, zbyl by v branži zlomek původního počtu. O tom, kolik má být zaměstnáno programátorů, rozhoduje trh a protože většina programátorů není geniálních a dokonalých, existují poučky a procesy, které umožňují uvařit aspoň něco a dodat na trh produkt s lidmi kteří jsou.
Tudíž se ten příspěvek sice tváří děsně prakticky, ale ve skutečnosti nic ne(vy)řeší kromě vlastního ega autora. Další věc je, že lidi, kteří jsou jenom umělci (praktici) a nemají žádný přesah do teoretické CS, nemohou obor nikam posunout, takže ta povýšenost je dost k ničemu. Debaty o principech dávají šanci třídit myšlenky a trochu si rozšířit obzor. Neznamená to, že kdo se jich účastní, není schopen banální odpovědi typu "někdy ano, jindy ne".
-
...
Jak konečně? To už bylo v první odpovědi (Ondřej Nekola). Kromě toho je Tebou adorovaný příspěvek vnitřně nekonzistentní - jestliže 99% programátotů jsou nemyslící lamy a měli by toho nechat, zbyl by v branži zlomek původního počtu. O tom, kolik má být zaměstnáno programátorů, rozhoduje trh a protože většina programátorů není geniálních a dokonalých, existují poučky a procesy, které umožňují uvařit aspoň něco a dodat na trh produkt s lidmi kteří jsou.
1) Ze to napsal Ondrej Nekola jsem si nevsim. Omlouvam se, psal to opravdu prvni.
2) Precti si laskave: https://cs.wikipedia.org/wiki/Hyperbola_(literatura) a nemel tady nesmysly.
Tudíž se ten příspěvek sice tváří děsně prakticky, ale ve skutečnosti nic ne(vy)řeší kromě vlastního ega autora. Další věc je, že lidi, kteří jsou jenom umělci (praktici) a nemají žádný přesah do teoretické CS, nemohou obor nikam posunout, takže ta povýšenost je dost k ničemu. Debaty o principech dávají šanci třídit myšlenky a trochu si rozšířit obzor. Neznamená to, že kdo se jich účastní, není schopen banální odpovědi typu "někdy ano, jindy ne".
Staci tedy zminit tuto pravdu. Nebo ji dat nejak najevo. A ne tady mlet polovinu vlakna(At uz tohoto, nebo podobnych) pi*oviny o getterech a setterech.
O tom presahu je to totalni blabol, ze vsech moznych stran. To nebudu ani rozebirat.
-
... pi*oviny o getterech a setterech.
Hele, neotírej si hubu o něco, čemu nerozumíš.
-
1) Ze to napsal Ondrej Nekola jsem si nevsim. Omlouvam se, psal to opravdu prvni.
Nejde o to, kdo to napsal první, ale jelikož už to někdo v diskusi napsal, bylo zbytečné to opakovat a debata šla dál.
2) Precti si laskave: https://cs.wikipedia.org/wiki/Hyperbola_(literatura) a nemel tady nesmysly.
To si číst nemusím. Právě proto, že jsem to vzal jako nadsázku, napsal jsem, že těch lidí by zbyl zlomek a ne že by jich zbylo pouhé procento. Bylo mi úplně jasné, že mě někdo chytí za slovo a bude mi to chtít otlouct o hlavu.
Staci tedy zminit tuto pravdu. Nebo ji dat nejak najevo. A ne tady mlet polovinu vlakna(At uz tohoto, nebo podobnych) pi*oviny o getterech a setterech.
Viz výše - "pravda" byla zjevena, netřeba ji opakovat.
O tom presahu je to totalni blabol, ze vsech moznych stran. To nebudu ani rozebirat.
Bez urážky - je tady pár lidí, od kterých bych podobnou reakci vzal, ale ti kupodivu v diskusi používají argumenty. Ty s tímto přístupem nikam nedojdeš.
-
Řekněme, že chci mít objekty čtverec, obdélník, kosočtverec a rovnoběžník. Je jasné, že s jednoduchou dědičností se daleko nedostanu. Proto obecný dotaz: existuje nějaké jiné paradigma, ve kterém lze vztahy mezi objekty vyjádřit lépe? Pokud ano, na jaký jazyk bych se měl podívat?
Preco chces vlastne dedit? Nestacia ti 4 structury bez dedicnosti?
To je dost nesmysl, bo code reuse. Jasně, šlo by to přes rozšíření (třeba výpočet obsahu), ale to je mimo původní otázku.
-
stvorec extends obdlznik je standardny fail lebo liskov substitutuion principle
Tak v tomto případě LSP perfektně funguje, máš v tom zmatek.
-
stvorec extends obdlznik je standardny fail lebo liskov substitutuion principle
Tak v tomto případě LSP perfektně funguje, máš v tom zmatek.
Funguje jenom za dalsich okolnosti. Viz vyse, uz se to tu rozebiralo.
-
@zboj
urcite nie
behavior fail
-
To je dost nesmysl, bo code reuse.
Prakticky každá firma (co jsem viděl), která měla zoufale neudržitelný kód ho měla zoufalí proto, že dědily kůli code reuse. Takže za mě fakt ne!
-
To je dost nesmysl, bo code reuse.
Prakticky každá firma (co jsem viděl), která měla zoufale neudržitelný kód ho měla zoufalí proto, že dědily kůli code reuse. Takže za mě fakt ne!
Jako by to neslo delat jak spatne, tak dobre... (rikam jako clovek, co inklinuje spis ke kompozici a typicky DI)
-
To je dost nesmysl, bo code reuse.
Prakticky každá firma (co jsem viděl), která měla zoufale neudržitelný kód ho měla zoufalí proto, že dědily kůli code reuse. Takže za mě fakt ne!
To je lidma, ne děděním.
-
@zboj
urcite nie
behavior fail
Určitě jo. V tomto případě to ani není težké na pochopení.
-
@zboj
urcite nie
behavior fail
Určitě jo. V tomto případě to ani není težké na pochopení.
Projed si historii diskuse. Problem nastane, kdyz mas mutable objekty.
-
@zboj
urcite nie
behavior fail
Určitě jo. V tomto případě to ani není težké na pochopení.
Dokonce koukam, ze to je defaultni priklad na wikipedii: https://en.wikipedia.org/wiki/Liskov_substitution_principle#A_typical_violation
-
@zboj
urcite nie
behavior fail
Určitě jo. V tomto případě to ani není težké na pochopení.
Projed si historii diskuse. Problem nastane, kdyz mas mutable objekty.
To ale platí obecně, nesouvisí to se čtvercema a obdélníkama.
-
Projed si historii diskuse. Problem nastane, kdyz mas mutable objekty.
To je vše o tom, že si někdo myslí, že čtverec je obdélník. Když pak zjistí, že některé vlastnosti na to nepasují, zavrhne dědění jako celek - s vaničkou vyleje i dítě. Přitom kdyby neudělal chybu v počátečním předpokladu, že čtverec == obdélník, tak by věděl, že tyto dvě třídy od sebe dědit nemohou.
Takže dědičnost je nutné používat tam, kde má smysl a nepoužívat tam, kde smysl nemá. Bez dědičnosti by naše aplikace v OOP byly mnohonásobně delší. A ten kdo tvrdí, že dědičnost nepoužívá, ji používá neustále.
-
Přitom kdyby neudělal chybu v počátečním předpokladu, že čtverec == obdélník, tak by věděl, že tyto dvě třídy od sebe dědit nemohou.
Bez problémů mohou, ve čtverci se napíše nový konstruktor a dva settery a vyřešeno. Problém s tím má pouze Barbara Liskov a pár teoretiků k tomu :)
-
Přitom kdyby neudělal chybu v počátečním předpokladu, že čtverec == obdélník, tak by věděl, že tyto dvě třídy od sebe dědit nemohou.
Bez problémů mohou, ve čtverci se napíše nový konstruktor a dva settery a vyřešeno. Problém s tím má pouze Barbara Liskov a pár teoretiků k tomu :)
Někteří diskutující by z obavy před porušením LSP nejraději tyhlety dědičnosti úplně zakázali a vyhodili z OOP.
-
@zboj
ja to lsp potom nechapem. vyjasni mi to:-) dik
-
Někteří diskutující by z obavy před porušením LSP nejraději tyhlety dědičnosti úplně zakázali a vyhodili z OOP.
Dědičnost objektů je nutná nikoliv postačující podmínka existence OOP. Nutný důsledek z toho je, že někteří diskutující vůbec nemají rádi OOP :) To jsme ale věděli dlouho ...
-
Někteří diskutující by z obavy před porušením LSP nejraději tyhlety dědičnosti úplně zakázali a vyhodili z OOP.
Dědičnost objektů je nutná nikoliv postačující podmínka existence OOP. Nutný důsledek z toho je, že někteří diskutující vůbec nemají rádi OOP :) To jsme ale věděli dlouho ...
Neni to nutna podminka. Muzes mit trebas OOP postavene na prototypech (nejsem fanda, ale reseni to je).
-
Neni to nutna podminka. Muzes mit trebas OOP postavene na prototypech (nejsem fanda, ale reseni to je).
Prototypy umožňují dědičnost z principu, byť je k dědění nutná rozsáhlá ruční práce.
Jazyk kde dědičnost opravdu není je hypotetická Java bez keyword extends.
-
Neni to nutna podminka. Muzes mit trebas OOP postavene na prototypech (nejsem fanda, ale reseni to je).
Prototypy umožňují dědičnost z principu, byť je k dědění nutná rozsáhlá ruční práce.
Jazyk kde dědičnost opravdu není je hypotetická Java bez keyword extends.
Samotné implements je taky dědičnost, byť trochu jiná.
-
Neni to nutna podminka. Muzes mit trebas OOP postavene na prototypech (nejsem fanda, ale reseni to je).
Prototypy umožňují dědičnost z principu, byť je k dědění nutná rozsáhlá ruční práce.
Jazyk kde dědičnost opravdu není je hypotetická Java bez keyword extends.
Mýlíš se. Dědičnost v Javě funguje i bez extends. Každá třída má předka.
-
Jinak bude vypadat objektový návrh v C++, jinak v Javě, jinak v Pythonu, jinak ve Smalltalku, jinak pro účely grafické, jinak pro účely matematické, jinak pro účely čmárání po obrazovce myší, jinak pro účely vykreslení něčeho plottrem, jinak pro účely algebraické, jinak pro účely geometrické a docela jinak, pokud by se mělo všechno výše uvedené nějak skloubit dohromady. A pak přijde někdo s požadavkem doplnění sférické geometrie a další bude chtít diferenciální a uvidíte, jak jste v ...
...a tomu se, milé děti, říká "znovupoužitelnost kódu v OOP" :)
-
...a tomu se, milé děti, říká "znovupoužitelnost kódu v OOP" :)
To je ale překvápko. Z různých zadání vypadla různá řešení :)
Možná jsem naivní ale mám pocit, že ta bublina nekritického obdivu OOP začíná trochu splaskávat. Jsem zvědavý, co za stříbrnou kulku přijde teď.
-
To je ale překvápko. Z různých zadání vypadla různá řešení :)
Přesněji řečeno vzájemně nekompatibilní, znovunepoužitelné implementace jedné a téže věci :)
-
Nicméně já bych se vrátil k původnímu dotazu a podpořil bych Kit-a: stude, koukni se na jazyky, kde se na dědičnost nehraje a místo toho se používají volně kombinovatelné protokoly. Největší šanci na mainstreamovost má asi Go.
Různé implementace podobného principu:
Go: https://tour.golang.org/methods/5
Elixir: http://elixir-lang.org/getting-started/protocols.html
Haskell: https://www.haskell.org/tutorial/classes.html
Rust: https://doc.rust-lang.org/book/traits.html
-
Možná jsem naivní ale mám pocit, že ta bublina nekritického obdivu OOP začíná trochu splaskávat. Jsem zvědavý, co za stříbrnou kulku přijde teď.
Splaskává pouze bublina vymyšlených nesplnitelných očekávání od OOP.
Nicméně já bych se vrátil k původnímu dotazu a podpořil bych Kit-a: stude, koukni se na jazyky, kde se na dědičnost nehraje a místo toho se používají volně kombinovatelné protokoly. Největší šanci na mainstreamovost má asi Go.
Zrovna Go to má takhle: "Dědí" se jenom interface, nejsou virtuální metody a předek tak nemůže zavolat metodu z potomka. Pomůže to k něčemu ?
-
Nicméně já bych se vrátil k původnímu dotazu a podpořil bych Kit-a: stude, koukni se na jazyky, kde se na dědičnost nehraje a místo toho se používají volně kombinovatelné protokoly. Největší šanci na mainstreamovost má asi Go.
Různé implementace podobného principu:
Go: https://tour.golang.org/methods/5
Elixir: http://elixir-lang.org/getting-started/protocols.html
Haskell: https://www.haskell.org/tutorial/classes.html
Rust: https://doc.rust-lang.org/book/traits.html
...a Swift
-
Zrovna Go to má takhle: "Dědí" se jenom interface, nejsou virtuální metody a předek tak nemůže zavolat metodu z potomka. Pomůže to k něčemu ?
Nedědí se nic. Předek nemůže volat metodu potomka, protože žádný předek a potomek neexistuje. Pomůže to k tomu, že se nebudou vést nekonečné debaty, jestli je čtverec potomek obdélníka nebo obdélník předem čtverce.
-
Zrovna Go to má takhle: "Dědí" se jenom interface, nejsou virtuální metody a předek tak nemůže zavolat metodu z potomka. Pomůže to k něčemu ?
Nedědí se nic. Předek nemůže volat metodu potomka, protože žádný předek a potomek neexistuje. Pomůže to k tomu, že se nebudou vést nekonečné debaty, jestli je čtverec potomek obdélníka nebo obdélník předem čtverce.
Když se vezme čtverec, obdélník, kosočtverec a rovnoběžník, tak dědičnost vytvoří "diamant". Nicméně i tak se dá třeba výpočet plochy napsat jen jednou.
-
Nedědí se nic. Předek nemůže volat metodu potomka, protože žádný předek a potomek neexistuje. Pomůže to k tomu, že se nebudou vést nekonečné debaty, jestli je čtverec potomek obdélníka nebo obdélník předem čtverce.
Dědičnost "nějak" opravdu funguje, problém čtvercoobdélník je v Go snad ještě horší :)
http://play.golang.org/p/Xe9AyAx-Bh
-
Projed si historii diskuse. Problem nastane, kdyz mas mutable objekty.
To je vše o tom, že si někdo myslí, že čtverec je obdélník. Když pak zjistí, že některé vlastnosti na to nepasují, zavrhne dědění jako celek - s vaničkou vyleje i dítě. Přitom kdyby neudělal chybu v počátečním předpokladu, že čtverec == obdélník, tak by věděl, že tyto dvě třídy od sebe dědit nemohou.
Nebo dědit mohou, ale musí být na to objektový systém připraven, příkladem být může CLOS (https://en.wikipedia.org/wiki/Circle-ellipse_problem#Change_the_programming_language).
-
Projed si historii diskuse. Problem nastane, kdyz mas mutable objekty.
To je vše o tom, že si někdo myslí, že čtverec je obdélník. Když pak zjistí, že některé vlastnosti na to nepasují, zavrhne dědění jako celek - s vaničkou vyleje i dítě. Přitom kdyby neudělal chybu v počátečním předpokladu, že čtverec == obdélník, tak by věděl, že tyto dvě třídy od sebe dědit nemohou.
Nebo dědit mohou, ale musí být na to objektový systém připraven, příkladem být může CLOS (https://en.wikipedia.org/wiki/Circle-ellipse_problem#Change_the_programming_language).
Nemohou, protože čtverec není obdélník a kružnice není elipsa. Jsou to sourozenci mající společné rodiče.
Proč tady musí být zatvrzelí odpůrci versus zatvrzelí zastánci dědičnosti? Je tak těžké pochopit, že někdy se dědičnost hodí a jindy ne? Že by sourozenecké třídy neměly mezi sebou dědit?
-
Já jen poukazuji na to, že v některých systémech, to lze snadno provést a nejsme omezeni jako v jiných systémech. Na problém čteverec-obdélník lze pohlížet oběma způsoby, nejde říct, že to jsou vždy jen sourozenci nebo vždy jen rodič-dítě.
-
Proč tady musí být zatvrzelí odpůrci versus zatvrzelí zastánci dědičnosti? Je tak těžké pochopit, že někdy se dědičnost hodí a jindy ne?
Protože code reuse. Mít čtyři různé objekty je teoreticky správné, ale prakticky nežádoucí a kontraproduktivní. Stejně ty čtyři různé objekty budou jenom boiler plate pro volání funkcí společných pro všechny čtyři varianty.
-
To je dost nesmysl, bo code reuse.
Prakticky každá firma (co jsem viděl), která měla zoufale neudržitelný kód ho měla zoufalí proto, že dědily kůli code reuse. Takže za mě fakt ne!
To je lidma, ne děděním.
Takže když programátorům zakážeme používat dědění, tak bude problém vyřešen, ne?
Někteří diskutující by z obavy před porušením LSP nejraději tyhlety dědičnosti úplně zakázali a vyhodili z OOP.
Kůli LSP bych to nedělal, ale ano hlásím se k těm, které by dědění zakázali.
-
Proč tady musí být zatvrzelí odpůrci versus zatvrzelí zastánci dědičnosti? Je tak těžké pochopit, že někdy se dědičnost hodí a jindy ne?
Protože code reuse. Mít čtyři různé objekty je teoreticky správné, ale prakticky nežádoucí a kontraproduktivní. Stejně ty čtyři různé objekty budou jenom boiler plate pro volání funkcí společných pro všechny čtyři varianty.
Přesně! Tady nejde o zatvrzelé odpůrce dědičnosti, ale o zatvrzelé zastánce dědičnosti pro nevhodná použití. Dědičnost na code reuse se používá proto, že máme blbý jazyky, který to lépe neuměj.
-
Přesně! Tady nejde o zatvrzelé odpůrce dědičnosti, ale o zatvrzelé zastánce dědičnosti pro nevhodná použití. Dědičnost na code reuse se používá proto, že máme blbý jazyky, který to lépe neuměj.
Je to přesně obráceně :) Dědičnost s virtuálními metodami je zobecnění postupu používaného na code reuse před OOP, dělalo se to tabulkou s pointery na specializované funkce, jakýsi předchůdce vtable.
-
Přesně! Tady nejde o zatvrzelé odpůrce dědičnosti, ale o zatvrzelé zastánce dědičnosti pro nevhodná použití. Dědičnost na code reuse se používá proto, že máme blbý jazyky, který to lépe neuměj.
Je to přesně obráceně :) Dědičnost s virtuálními metodami je zobecnění postupu používaného na code reuse před OOP, dělalo se to tabulkou s pointery na specializované funkce, jakýsi předchůdce vtable.
Ano. A je to prostě zlo. Vzor template method je tomu korunou.
-
Dědičnost "nějak" opravdu funguje, problém čtvercoobdélník je v Go snad ještě horší :)
http://play.golang.org/p/Xe9AyAx-Bh
To není dědičnost, ale skládání. To si bohužel taky dost lidí dneska plete...
Since we then defined Car as an anonymous field in Ferrari, the latter class automatically can call on all the visible behaviors/methods of the anonymous field type. So here, we have not subclassed a parent class, but composed it.
http://golangtutorials.blogspot.cz/2011/06/inheritance-and-subclassing-in-go-or.html
Jaký problém tam vidíš? Já žádný. Rozhodně ne takový, aby vedl k plamenným debatám o tom, jestli je čtverec obdélník...
-
Nemohou, protože čtverec není obdélník a kružnice není elipsa. Jsou to sourozenci mající společné rodiče.
Pokud tvrdíš, že nějaké entity "jsou sourozenci", tak implicitně používáš nějaké uspořádání. Jaké? Jsi si jistý, že ten, proti komu argumentuješ, nepracuje s nějakým jiným uspořádáním? Nemíjíte se právě proto?
Podle mě totiž v tomhle je doopravdy ten zakopaný pes: každá OOP učebnice pracuje s tím, že to uspořádání je to, co intuitivně můžeme nazvat "obecnost" (pojmu): Žárovka je Svítidlo, Pes je Savec atd. A všechny tyhle přiblblé debaty pramení z toho, že tohle uspořádání sice výborně funguje v teorii, na ideálních příkladech, ale v praxi máš jiná kritéria => jiné uspořádání => jinou hierarchii.
Nebo snad umíš precizně a jednoznačně nadefinovat jediné správné uspořádání objektů, které by vytvořilo tu jedinou správnou hierarchii dědičnosti?
-
To je dost nesmysl, bo code reuse.
Prakticky každá firma (co jsem viděl), která měla zoufale neudržitelný kód ho měla zoufalí proto, že dědily kůli code reuse. Takže za mě fakt ne!
To je lidma, ne děděním.
Takže když programátorům zakážeme používat dědění, tak bude problém vyřešen, ne?
Někteří diskutující by z obavy před porušením LSP nejraději tyhlety dědičnosti úplně zakázali a vyhodili z OOP.
Kůli LSP bych to nedělal, ale ano hlásím se k těm, které by dědění zakázali.
Tak. Developers by se neměli množit.
-
Tak. Developers by se neměli množit.
Množit se můžou, ale majetek by měli odkazovat charitě!
-
Tak. Developers by se neměli množit.
Množit se můžou, ale majetek by měli odkazovat charitě!
Vole! ;D
(no offence!)
PS: samozřejmě, že charitě, kde jsem v nějaké "dozorčí" radě!
-
...
Nebo snad umíš precizně a jednoznačně nadefinovat jediné správné uspořádání objektů, které by vytvořilo tu jedinou správnou hierarchii dědičnosti?
To mi připomíná jednu úžasnou Sovákovu hlášku z "Kam čert nemůže" ... (něco jako až budu mít FAKT čas, tak se tomu budu opravdu věnovat)
-
Ano. A je to prostě zlo. Vzor template method je tomu korunou.
Proč zlo ? IMHO jinak než použitím pointerů na funkce/metody nelze code reuse realizovat. Nebo ano ?
Since we then defined Car as an anonymous field in Ferrari, the latter class automatically can call on all the visible behaviors/methods of the anonymous field type. So here, we have not subclassed a parent class, but composed it.
Možná tomu říkají skládání, výsledek z hlediska metod je stejný jako dědění nevirtuálních metod v OOP.
Proto nějaké problémy zůstávají, do čtverce vložíte obdélník a zkriplíte SetWidth SetHeight, úplně stejně jako v OOP.
-
Prymek:
"To není dědičnost, ale skládání"
Ano? Čím se to liší od dědičnosti v C++? Tam také když dědíš, tak vlastně do struktury přidáš data předka - a můžeš volat metody předka a když to dáš do metody, co očekává předka, tak to funguje..... Dědičnost ve většině jazyků prakticky funguje jako kompozice s nějakým syntatickým cukrem. To, co je v GO se chová jako dědičnost a tedy podle duck typing je dědičnost :-)
---
Jinak k debatě, jestli čtevere je nebo není obdélník - tak to samozřejmě závisí na definici obdélníku. Pokud ho definujeme jako:
1) "Čtyřuhelník, který má všechny úhly pravé", pak evidentně čtverec obdélník je.
Pokud ho ovšem definujeme jako:
2) "Čtyřuhelník, který má všechny úhly pravé a může si měnit šířku a výšku."
- Pak evidentně čtverec obdélník není.
Takže záleží, pro co píšeme program a jakou definici obdélníku používáme. Dokážu si představit i řešení, kdy mám obdélník dle definice 1) a z něj podědím jak čtverec, tak obdélník dle definice 2).
-
Možná tomu říkají skládání, výsledek z hlediska metod je stejný jako dědění nevirtuálních metod v OOP.
Proto nějaké problémy zůstávají, do čtverce vložíte obdélník a zkriplíte SetWidth SetHeight, úplně stejně jako v OOP.
Nejde o to, kdo čemu jak říká, ale o to, že dědění a skládání je jiný koncept. Rozdíl zhruba odpovídá tomu "mít" a "být", co tady zaznělo. A důsledek je mj. větší volnost: nikdo se nebude do krve hádat, jestli struktura může mít nějakou položku, tam je snad každýmu jasný, že to je buřt. To je jeden z příjemných čistě praktických důsledků. Jiné důsledky jsou třeba, že tu vnitřní položku můžu snadno vracet, vyměnit, nahradit úplně jinou strukturou atd. atd. Co přesně z toho platí pro Go, nevím, o tom se nechci hádat, znám ho jenom z rychlíku.
-
Proč zlo ? IMHO jinak než použitím pointerů na funkce/metody nelze code reuse realizovat. Nebo ano ?
Generika? C++ šablony? Makra? Code reuse se přece nemusí omezovat na spustitelný kód. Stačí když potřebné úpravy zvládne překladač sám.
[/quote]
-
Ano? Čím se to liší od dědičnosti v C++?
Třeba tím, že můžeš dělat "inferenci" (nevím, jaký je v tomhle kontextu správný termín) - pokud existují dané metody, pak je splněn daný protokol. Klidně i v případě, že jsi ty metody napsal deset let před tím a protokol definuješ až teď. (Nejsem si jistý, jestli tohle pro Go platí, jde mi o princip, ne o konkrétní jazyk)
Víc než s deděním bych to srovnával se starým dobrým mixinem.
-
Generika? C++ šablony? Makra? Code reuse se přece nemusí omezovat na spustitelný kód. Stačí když potřebné úpravy zvládne překladač sám.
...anebo právě ty protokoly. Chci generický bubblesort? Stačí mi, že pro strukturu existují funce compare a swap. Jak si je překladač najde, kam je uloží, kde je vezme, jak je spáruje s datovou strukturou, mi může být úplně buřt, já mám v kódu prostě
compare(jakakoliv_struktura,idx1,idx2)
a basta. O tom přece code reuse je - dělat věci genericky, za použití nějakého API. Callbacky jsou jenom jedna z možností implementace. Bohužel, když má někdo mozek nastavený na C, nebo nic jiného nepotkal, tak si to ani nedokáže představit...
-
Projed si historii diskuse. Problem nastane, kdyz mas mutable objekty.
To je vše o tom, že si někdo myslí, že čtverec je obdélník. Když pak zjistí, že některé vlastnosti na to nepasují, zavrhne dědění jako celek - s vaničkou vyleje i dítě. Přitom kdyby neudělal chybu v počátečním předpokladu, že čtverec == obdélník, tak by věděl, že tyto dvě třídy od sebe dědit nemohou.
Nebo dědit mohou, ale musí být na to objektový systém připraven, příkladem být může CLOS (https://en.wikipedia.org/wiki/Circle-ellipse_problem#Change_the_programming_language).
Nemohou, protože čtverec není obdélník a kružnice není elipsa. Jsou to sourozenci mající společné rodiče.
Proč tady musí být zatvrzelí odpůrci versus zatvrzelí zastánci dědičnosti? Je tak těžké pochopit, že někdy se dědičnost hodí a jindy ne? Že by sourozenecké třídy neměly mezi sebou dědit?
To je megahovadina, čtverec je z definice obdélník.
-
Ještě jedna poznámka a už končím :)
OOP může úplně s klidem vést i k tomu, že právě code reuse není možný - právě kvůli té myšlence spojení dat a metod. Když mám entitu reprezentovanou jenom daty - a navíc ještě pokud možno jednoduchými strukturami (FP přístup), tak třeba kde co můžu mít reprezentované dvojicí. A můžu mít jenom jednu funkci swap, která prvky dvojice prohodí. Pak mi stačí už jenom třeba makrem říct, že pro obdélník je otočení, zatímco pro jméno je to převod z Češtiny do Maďarštiny ;)
Zkuste si tohle rozchodit v OOP - ideálně pomocí interface ISwappable, factory MakeSwappable, databáze mapující operaci swap na jeho význam pro různé objekty (samozřejmě hodnotou je pak konstanta z public final class SwappableSematics). A pak můžeme rozjet flame na téma, jestli je CzechToHungaryConvertor předek nebo dědic Generic2DObjectRotatorImmutable ;)
-
Nemohou, protože čtverec není obdélník a kružnice není elipsa. Jsou to sourozenci mající společné rodiče.
Pokud tvrdíš, že nějaké entity "jsou sourozenci", tak implicitně používáš nějaké uspořádání. Jaké? Jsi si jistý, že ten, proti komu argumentuješ, nepracuje s nějakým jiným uspořádáním? Nemíjíte se právě proto?
Je tedy správné se zeptat: V jakém kontextu chceš srovnávat čtverec a obdélník? Podle geometrické definice je čtverec skutečně speciálním případem obdélníku. Jenže je ten geometrický kontext skutečně tím kontextem, který chceme používat v aplikaci?
Podle mě totiž v tomhle je doopravdy ten zakopaný pes: každá OOP učebnice pracuje s tím, že to uspořádání je to, co intuitivně můžeme nazvat "obecnost" (pojmu): Žárovka je Svítidlo, Pes je Savec atd. A všechny tyhle přiblblé debaty pramení z toho, že tohle uspořádání sice výborně funguje v teorii, na ideálních příkladech, ale v praxi máš jiná kritéria => jiné uspořádání => jinou hierarchii.
Že Pes je Savec, to víme všichni a je to i v učebnicích OOP. Opět záleží na kontextu - někdy to prostě platit nemusí. Například hot dog není savec.
Nebo snad umíš precizně a jednoznačně nadefinovat jediné správné uspořádání objektů, které by vytvořilo tu jedinou správnou hierarchii dědičnosti?
Ne, to nelze. Mohu však nadefinovat v požadovaném kontextu, zda je vazba rodičovská, sourozenecká nebo žádná. Vždy se dopustíme nějaké nepřesnosti, ale rozhodující je, zda nám v daném kontextu pomůže ve vývoji nebo nám naopak bude házet klacky pod nohy.
Výrok "1+1=2" přece také neplatí vždy, ale pouze v určitém oboru matematiky. Stejně tak i tvrzení, že čtverec je obdélník, platí jen v určitém oboru geometrie. Nelze je bezhlavě aplikovat v každém kontextu. Proto když tvrdím, že čtverec není obdélník, snažím se tím vymanit ze skrytých závislostí, které by mi mohly způsobit bolení hlavy například v kreslicím programu.
-
Je tedy správné se zeptat: V jakém kontextu chceš srovnávat čtverec a obdélník? [...] Ne, to nelze. Mohu však nadefinovat v požadovaném kontextu, zda je vazba rodičovská, sourozenecká nebo žádná.
Proto nemůžeš tvrdit, že to jsou sourozenci.
-
Je tedy správné se zeptat: V jakém kontextu chceš srovnávat čtverec a obdélník? [...] Ne, to nelze. Mohu však nadefinovat v požadovaném kontextu, zda je vazba rodičovská, sourozenecká nebo žádná.
Proto nemůžeš tvrdit, že to jsou sourozenci.
Zdá se, že jsi přesně vystihl, co jsem měl na mysli a chytl jsi mě na větě, která byla zřejmě až příliš zjednodušující.
Tedy kauza čtverec a obdélník. Bez kontextu v zásadě vůbec nemůžeme rozhodnout, zda:
- čtverec je potomkem obdélníka
- čtverec je sourozencem obdélníka, mají tedy společného rodiče
- čtverec a obdélník nemají nic společného
Teprve kontext nám může pomoci se zodpovědně rozhodnout, kterou vazbu máme zvolit. Pokud zvolíme chybně, tak porušíme DRY nebo budeme řešit diamanty.
-
OOP je zlo. Ja som sa ho tiez dlho drzal. Potom som si vsak uvedomil ze oproti modularnemu programovaniu neprinasa nic navyse. Zato sposobuje neustale bobtnanie kodu a spomalovanie sa aplikacii. Dnesne aplikacie robia +- to iste ako aplikacie 20 rokov dozadu (s vynimkou hier a multimedii) zato vsak zeru 100 nasobne viac prostriedkov. Tie iste aplikacie mohli byt na dnesnych pocitacoch 100x rychlejsie keby lepsie vyuzivali moznosti hardware. Problem je uz v celkovej fylozofia OOP, abstrakcia, rozne VM, medzivrstvy, kniznice, frameworky a lepenie kodu.
-
chytl jsi mě na větě, která byla zřejmě až příliš zjednodušující.
Příliš silná a proto neplatná. A stejně neplatná je představa, o které jsme se tady bavili nedávno v podobným tématu - že totiž OOP nějakým způsobem odpovídá tomu, jak svět vnímáme, nebo dokonce jaký svět objektivně je (jakási programátorská "ontologie" ve filosofickém smyslu). A protože to není pravda, padá i další bláhová představa o znovupoužitelnosti - že jednou nějaká dobrá duše napíše objekt Person a od té doby se bude už jenom používat.
Kvalitní implementace OOP s posíláním zpráv, opravdovým dynamic dispatchem, možností customizovat existující objekty (kategorie v Objective C) atd. se tomu můžou trochu blížit, protože tam není potřeba to s děděním přehánět. Ale pro mainstreamové OOP, kde se málem o ničem jiným než o dědičnosti nemluví, je to absolutní scifi, návnada na hejly.
-
A stejně neplatná
= kvůli stejným příčinám
-
OOP je zlo. Ja som sa ho tiez dlho drzal. Potom som si vsak uvedomil ze oproti modularnemu programovaniu neprinasa nic navyse. Zato sposobuje neustale bobtnanie kodu a spomalovanie sa aplikacii. Dnesne aplikacie robia +- to iste ako aplikacie 20 rokov dozadu (s vynimkou hier a multimedii) zato vsak zeru 100 nasobne viac prostriedkov. Tie iste aplikacie mohli byt na dnesnych pocitacoch 100x rychlejsie keby lepsie vyuzivali moznosti hardware. Problem je uz v celkovej fylozofia OOP, abstrakcia, rozne VM, medzivrstvy, kniznice, frameworky a lepenie kodu.
IMO jeden veliký žrout výkonu v OOP vychází z myšlenky seskupit všechny parametry objektu na jedno místo. Pak když se s objektem nějak pracuje, tak se přistupuje třeba jenom k jednomu intu, ale natáhne se celý řádek cache. Práce s polem takových objektů pak znamená pěkně řídký přístup do paměti. A pokud jsou ty objekty větší, tak se najednou dá použít třeba jen polovina cache.
Jasně, v 80% kódu je to putna. Ale u takových virtuálních funkcí nikoho moc nepřekvapí, když se doví že dělají bugr v pipeline procesoru. Jaký binec může udělat jednoduché seskupení více atributů do jedné struktury je hodně překvapivé.
-
Podľa mňa sa dedičnosť hodí hlavne tam kde treba dynamic dispatching. Hierarchiu objektov prispôsobím tomu čo s danými objektami chcem robiť. Ak nepotrebujem virtuálne funkcie tak kľudne môžem použiť kompozíciu objektov, mixiny, templaty ...
Ak nevieme čo s danými objektami bude robiť tak sa blbo navrhuje riešenie.
-
IMO jeden veliký žrout výkonu v OOP vychází z myšlenky seskupit všechny parametry objektu na jedno místo. Pak když se s objektem nějak pracuje, tak se přistupuje třeba jenom k jednomu intu, ale natáhne se celý řádek cache. Práce s polem takových objektů pak znamená pěkně řídký přístup do paměti. A pokud jsou ty objekty větší, tak se najednou dá použít třeba jen polovina cache.
O tom je hezký a názorný článek tady:
http://harmful.cat-v.org/software/OO_programming/_pdf/Pitfalls_of_Object_Oriented_Programming_GCAP_09.pdf
Jinak aby nedošlo k nedorozumnění, nejsem odpůrcem OOP, myslím, že OOP je to nejpraktičtější, co aktuálně máme a na většinu aplikací je to správná volba. Jasně, má to svoje problémy, jenže ostatní neOOP přístupy mají svoje problémy taky.
-
Podľa mňa sa dedičnosť hodí hlavne tam kde treba dynamic dispatching.
Žádný mainstreamový jazyk skutečný dynamic dispatch nemá. Dynamic dispatch znamená, že umím na libovolnou zprávu (předem neznámou) odpovědět "Sorry, tohle neumím".
-
Žádný mainstreamový jazyk skutečný dynamic dispatch nemá. Dynamic dispatch znamená, že umím na libovolnou zprávu (předem neznámou) odpovědět "Sorry, tohle neumím".
Tohle je IMO jedině dobře :) Tohle totiž znamená taky to že překlepy v kódu se pořádně nekontrolují při překladu ale až při běhu. Nevím jak jiné, ale takový Python je v tomhle jazyk z nejhlubšího pekla.
-
Tohle je IMO jedině dobře :) Tohle totiž znamená taky to že překlepy v kódu se pořádně nekontrolují při překladu ale až při běhu. Nevím jak jiné, ale takový Python je v tomhle jazyk z nejhlubšího pekla.
To není pravda. Objective C dává warningy. S pythonem souhlas.
-
...
Nemusí to byť "skutočný" dynamic dispatch, aj zjednodušená verzia ako v C++/D/C#/atd má svoje využitie.
-
Generika? C++ šablony? Makra? Code reuse se přece nemusí omezovat na spustitelný kód. Stačí když potřebné úpravy zvládne překladač sám.
Dokážu si představit že kompilátor místo dědičnosti a virtuálních metod jednoduše definice a kód všech proměnných a nevirtuálních metod zkopíruje z předka, dneska máme paměti dost. Problémy ale zůstanou stejné, změní se to jenom technicky.
Třeba tím, že můžeš dělat "inferenci" (nevím, jaký je v tomhle kontextu správný termín) - pokud existují dané metody, pak je splněn daný protokol. Klidně i v případě, že jsi ty metody napsal deset let před tím a protokol definuješ až teď. (Nejsem si jistý, jestli tohle pro Go platí, jde mi o princip, ne o konkrétní jazyk)
To je v problému čtvercoobdélník splněno :) Čtverec má dané metody obdélníka, tedy je splněn daný protokol, problém zůstává.
OOP je zlo.
Mainstream OOP je zobecněný postup který se dělal ručně před vznikem OOP, nemůže to být zlo samo o sobě.
Zlem to začalo být až tehdy, když se OOP začalo nadužívat na nesmysly. Typický nesmysl je místo pole int udělat kolekci s pointery na objekty Int, samozřejmě každý objekt má vlastní alokaci na haldě, aby zlo bylo maximální.
-
To je v problému čtvercoobdélník splněno :) Čtverec má dané metody obdélníka, tedy je splněn daný protokol, problém zůstává.
To byla odpověď na to, čím se to liší od C++. V C++ nemůžeš existující objekt prohlásit za splňující protokol.
-
Dokážu si představit že kompilátor místo dědičnosti a virtuálních metod jednoduše definice a kód všech proměnných a nevirtuálních metod zkopíruje z předka, dneska máme paměti dost. Problémy ale zůstanou stejné, změní se to jenom technicky.
Prosím, zapomeň na cokoliv co připomíná dědičnost. Pořádný code reuse znamená že stejný kód zvládne pracovat s věcmi co ani nikdy neležely ve stejném adresovém prostoru, natož aby se dalo mluvit o nějakém společném předkovi.
Protokoly o kterých tu mluví MP se se zatnutými zuby dají trochu přirovnat k abstraktním třídám, ale i tohle je zavádějící.
Typický nesmysl je místo pole int udělat kolekci s pointery na objekty Int, samozřejmě každý objekt má vlastní alokaci na haldě, aby zlo bylo maximální.
Tohle je jen bezvýznamná drobnost. Opravdové zlo leží někde jinde.
-
Prýmek: To, jak odlišuješ dědičnost od toho, co je v GO, ale záleží na definici dědičnosti. Pokud do pojmu dědičnost zahrneš duck-typing, pak to, co je v GO splňuje definici dědičnosti - umí to kachní protokol, je to kachna. S tím, že to má všechny výhody a nevýhody duck-typingu - tj. objekty umí něco navíc, co s třídní dědičností nelze a platím za to horší možností syntaktické kontroly.
---
Code reuse samozřejmě neznamená jen dědičnost. Dědičnost je jedna z metod. A ten kdo umí dědičnost a neumí jiné metody bude obhajovat dědičnost, a kdo neumí dědičnost a umí jiné metody ji bude hanit. A oba se budou hádat, že "to druhé" je cesta do pekla. Dobrý programátor prostě použije vhodně daný kontext a max si zanadává - todle by šlo v támdletom jazyku napsat elegantnějc. Já takhle střídám několik jazyků a rozstu z toho dost.
---
"To je v problému čtvercoobdélník splněno (http://forum.root.cz/Smileys/default/smiley.gif) Čtverec má dané metody obdélníka..."
Jak jsem již psal, to může a nemusí být pravda. Pokud obdélník nadefinuji jako něco, co umí změnit jednu stranu nezávisle na druhé, tak čtverec definici obdélníka prostě nesplňuje a dané metody mít nemůže.
-
Aby tu nebylo jen pusté filosofování, zde je "protocol-oriented" kód (ať to není příliš dlouhé, tak jen pro nejobecnější a nejspeciálnější případ):
protocol QuadrilateralWithParallelOppositeSides {
var side1:Double { get }
var side2:Double { get }
var skew:Double { get }
}
extension QuadrilateralWithParallelOppositeSides {
func area() -> Double {
return side1 * side2 * cos(skew)
}
}
struct Parallelogram : QuadrilateralWithParallelOppositeSides {
var side1:Double
var side2:Double
var skew:Double
init(side1 s1:Double, side2 s2:Double, skew s:Double) {
side1 = s1
side2 = s2
skew = s
}
}
struct Square : QuadrilateralWithParallelOppositeSides {
var side1:Double
var side2:Double { return side1 }
var skew:Double { return 0 }
init(side s1:Double) {
side1 = s1
}
}
let figure1 = Parallelogram(side1: 2, side2: 3, skew: M_PI_4)
let figure2 = Square(side: 3)
var list:Array<QuadrilateralWithParallelOppositeSides> = [ figure1, figure2 ]
print(list.map { $0.area() })
Jak je hezky vidět, metoda pro výpočet obsahu se definuje jen jednou. O různé invarianty se postarají "computed properties". Jednoduché, elegantní a rozšiřitelné :)
-
OOP je zlo ... sposobuje neustale bobtnanie kodu a spomalovanie sa aplikacii ... Problem je uz v celkovej fylozofia OOP, abstrakcia, rozne VM, medzivrstvy, kniznice, frameworky a lepenie kodu.
Jak bys mi chtěl zdůvodnit, že dříve napsané aplikace, které jsem přepsal do OOP, se mi zkrátily a zrychlily? A podobně dopadají i cizí programy, které refaktoruji do OOP, abych do nich mohl začít dělat své modifikace.
Problémy s OOP nehledej v bobtnání kódu, abstrakci apod. Hledej je v chybně pochopeném OOP.
-
Co se tyka OOP, dedicnosti a Go.. tady se dost zapomina, ze se dedi data, nikoli funkce (funkce neni treba dedit - bud jsou polymorfni nebo ne, a to je dane fakticky tim, jake jine funkce volaji (pripadne k jakym datum pristupuji ale to je jen specialni pripad volani)). Nejlepsi je IMHO se ke "klasickemu" OOP (C++, Java..) postavit tak, ze jde o tri ruzne abstrakce:
- Zapouzdreni
- Dedicnost
- Polymorfismus
Vsechny tyhle vlastnosti Go ma, ale misto toho, aby je michalo dohromady jako "tridy", tak je ma zvlast:
- Moduly
- V definici struktury je mozne primo vlozit jinou strukturu, bez kvalifikace (jenom jednu - odpovida jednonasobne dedicnosti)
- Interfacy (nebo tez protokoly - lze o objektu prohlasit, ze splnuje nejaky interface, napsat funkci, ktera predpoklada interface atd.)
IMHO je to tak lepsi. Mate tri ortogonalni koncepty a muzete si je smichat jak je libo.
-
Aby tu nebylo jen pusté filosofování, zde je "protocol-oriented" kód (ať to není příliš dlouhé, tak jen pro nejobecnější a nejspeciálnější případ):
...
Jak je hezky vidět, metoda pro výpočet obsahu se definuje jen jednou. O různé invarianty se postarají "computed properties". Jednoduché, elegantní a rozšiřitelné :)
Zdá se mi to, nebo ten příklad je ve skutečnosti zbytečně komplikovaný a má daleko k eleganci?
-
Co se tyka OOP, dedicnosti a Go.. tady se dost zapomina, ze se dedi data, nikoli funkce (funkce neni treba dedit - bud jsou polymorfni nebo ne, a to je dane fakticky tim, jake jine funkce volaji (pripadne k jakym datum pristupuji ale to je jen specialni pripad volani)). Nejlepsi je IMHO se ke "klasickemu" OOP (C++, Java..) postavit tak, ze jde o tri ruzne abstrakce:
- Zapouzdreni
- Dedicnost
- Polymorfismus
Vsechny tyhle vlastnosti Go ma, ale misto toho, aby je michalo dohromady jako "tridy", tak je ma zvlast:
- Moduly
- V definici struktury je mozne primo vlozit jinou strukturu, bez kvalifikace (jenom jednu - odpovida jednonasobne dedicnosti)
- Interfacy (nebo tez protokoly - lze o objektu prohlasit, ze splnuje nejaky interface, napsat funkci, ktera predpoklada interface atd.)
IMHO je to tak lepsi. Mate tri ortogonalni koncepty a muzete si je smichat jak je libo.
To "vkládání" struktur není moc rozumné, někdy má podtřída (díky invariantům, viz výše) méně dat než rodič.
-
Zdá se mi to, nebo ten příklad je ve skutečnosti zbytečně komplikovaný a má daleko k eleganci?
Já bych řekl, že je to vcelku elegantní. Maximálně trochu ukecané. Ale samozřejmě výpočet obsahu je moc jednoduchý problém na to, aby tam ta elegance vynikla.
-
Aby tu nebylo jen pusté filosofování, zde je "protocol-oriented" kód (ať to není příliš dlouhé, tak jen pro nejobecnější a nejspeciálnější případ):
...
Jak je hezky vidět, metoda pro výpočet obsahu se definuje jen jednou. O různé invarianty se postarají "computed properties". Jednoduché, elegantní a rozšiřitelné :)
Zdá se mi to, nebo ten příklad je ve skutečnosti zbytečně komplikovaný a má daleko k eleganci?
Kde se dá zjednodušit?
-
Aby tu nebylo jen pusté filosofování, zde je "protocol-oriented" kód
1) Takhle to jde v OOP také, samozřejmě.
2) Nejsou tam settery, ty právě způsobovaly ten hlavní problém. Konkrétně ve struct Square nejsou settery na side1 a side2. Immutable objekty neberu jako řešení.
-
Immutable objekty neberu jako řešení.
Tohle je IMO chyba. Schválně se při programování občas zastav a zamysli se kolik objektů upravuješ a kolik jich vytvoříš a pak jenom předáš. U mně ty immutable objekty jednoznačně vedou a to píšu hlavně v C++, které na to nějak extra zatížené není.
Immutable objekty řeší daleko víc věcí než jenom tohle. Zásadně mění třeba způsob jak synchronizovat vlákna.
Mimochodem, ty geometrické tvary jsou matematické objekty. Na matematické objekty sedne immutable jako zadek na hrnec.
-
Aby tu nebylo jen pusté filosofování, zde je "protocol-oriented" kód
1) Takhle to jde v OOP také, samozřejmě.
2) Nejsou tam settery, ty právě způsobovaly ten hlavní problém. Konkrétně ve struct Square nejsou settery na side1 a side2. Immutable objekty neberu jako řešení.
1) Nejde (kromě C++, to je na jinou debatu).
2) Právě neměnnost objektů je velká výhoda, i když v tomto případě jde spíše o to, jak se používají protokoly.
-
Mimochodem, ty geometrické tvary jsou matematické objekty. Na matematické objekty sedne immutable jako zadek na hrnec.
Záleží na aplikaci, když to bude geometrický tvar v počítačové hře, tak s immutable moc nepochodíš, tvar a poloha objektů se mění neustále.
-
Tohle je IMO chyba. Schválně se při programování občas zastav a zamysli se kolik objektů upravuješ a kolik jich vytvoříš a pak jenom předáš. U mně ty immutable objekty jednoznačně vedou a to píšu hlavně v C++, které na to nějak extra zatížené není.
Immutable objekty řeší daleko víc věcí než jenom tohle. Zásadně mění třeba způsob jak synchronizovat vlákna.
Většinu objektů píšu hlavně proto že potřebuji mutable data a udržet data při změnách konzistentní, nepřekvapivě. Kdyby věděl, že čtvercoobdélníky budou immutable, je pravděpodobné že bych objekty vůbec nepoužil. Synchronizaci vláken mám vyřešenou k dokonalosti.
1) Nejde (kromě C++, to je na jinou debatu).
2) Právě neměnnost objektů je velká výhoda, i když v tomto případě jde spíše o to, jak se používají protokoly.
Protokoly nejdou. Napsat čtyři potomky, to vše bez setterů, jde skoro všude.
Výhoda neměnnosti objektů je vykoupena nevýhodou jejich neustálého kopírování a měnění pointerů na novou kopii a to přináší zase jiné problémy.
-
Většinu objektů píšu hlavně proto že potřebuji mutable data a udržet data při změnách konzistentní, nepřekvapivě. [...] a měnění pointerů na novou kopii a to přináší zase jiné problémy.
Tohle mi přijde jakože s immutable objekty nemáš moc praktických zkušeností - z jazyků, kde se používají hodně. Protože právě immutable data nemůžou být nekonzistentní z principu, tímhle vůbec není potřeba se zabývat, tenhle problém vůbec neexistuje.
Stejně tak pointery - ty se právě v "ne-mutable" jazycích nepoužívají vůbec, protože k tomu není žádný důvod. Prostě kde mají být data, tam jsou data, a program je jako výrobní linka - něco jde dovnitř, něco jde ven.
Používání pointerů na jedna data na x místech programu je právě to peklo, to největší zlo OOP.
-
s immutable objekty
Pardon, tady jsem myslel spíš "data" než "objekty" ve smyslu OOP.
-
Zdá se mi to, nebo ten příklad je ve skutečnosti zbytečně komplikovaný a má daleko k eleganci?
Kde se dá zjednodušit?
Tento jazyk neznám, ale vidím tam funkci s nesmyslným názvem area(), která je vložena do nějakého rodiče obou objektů. Něco podobného dělají v PHP traits. Ovšem už ze zápisu té funkce vidím, že u čtverce zbytečně počítá cos(skew) - je vždy roven jedné. Raději napíši do každé třídy jiný vzoreček pro výpočet obsahu, než abych do třídy Čtverec dával atribut skew, který tam sémanticky vůbec nepatří. Místo jednoho atributu tam máš tři atributy - z toho dva jsou zcela zbytečné. Čtverec má jen jeden atribut a tím je délka strany.
interface Areable {
Double area()
}
struct Parallelograma : Areable {
var side1: Double
var side2: Double
var skew: Double
init(side1 s1: Double, side2 s2: Double, skew s: Double) {
side1 = s1
side2 = s2
skew = s
}
func area() -> Double {
return side1 * side2 * cos(skew)
}
}
struct Square : Areable {
var side: Double
init(side s1: Double) {
side = s1
}
func area() -> Double {
return side * side
}
}
let figure1 = Parallelogram(side1: 2, side2: 3, skew: M_PI_4)
let figure2 = Square(side: 3)
var list:Array<Areable> = [ figure1, figure2 ]
print(list.map { $0.area() })
Jak vidíš, zápis je ještě kratší, ještě jednodušší a pro mnohé i čitelnější.
-
Záleží na aplikaci, když to bude geometrický tvar v počítačové hře, tak s immutable moc nepochodíš, tvar a poloha objektů se mění neustále.
Tohle je zajímavá otázka. Je lepší mít mutable scénu, upravovat ji a řešit synchronizaci? Nebo je lepší immutable scéna, update vytvoří novou, jen zrecykluje všechno co se nezměnilo?
Já vidím to immutable řešení jako podstatně elegantnější a i celkem efektivní. A není to jen moje teoretizování. Nenašel jsem přednášku kohosi od Epicu, kde jsem to slyšel poprvé, ale něco podobného zaznělo i tu https://www.youtube.com/watch?v=1PhArSujR_A (https://www.youtube.com/watch?v=1PhArSujR_A).
-
Tohle je IMO chyba. Schválně se při programování občas zastav a zamysli se kolik objektů upravuješ a kolik jich vytvoříš a pak jenom předáš. U mně ty immutable objekty jednoznačně vedou a to píšu hlavně v C++, které na to nějak extra zatížené není.
Immutable objekty řeší daleko víc věcí než jenom tohle. Zásadně mění třeba způsob jak synchronizovat vlákna.
Většinu objektů píšu hlavně proto že potřebuji mutable data a udržet data při změnách konzistentní, nepřekvapivě. Kdyby věděl, že čtvercoobdélníky budou immutable, je pravděpodobné že bych objekty vůbec nepoužil. Synchronizaci vláken mám vyřešenou k dokonalosti.
1) Nejde (kromě C++, to je na jinou debatu).
2) Právě neměnnost objektů je velká výhoda, i když v tomto případě jde spíše o to, jak se používají protokoly.
Protokoly nejdou. Napsat čtyři potomky, to vše bez setterů, jde skoro všude.
Výhoda neměnnosti objektů je vykoupena nevýhodou jejich neustálého kopírování a měnění pointerů na novou kopii a to přináší zase jiné problémy.
Pointerů? U struct?
-
Zdá se mi to, nebo ten příklad je ve skutečnosti zbytečně komplikovaný a má daleko k eleganci?
Kde se dá zjednodušit?
Tento jazyk neznám, ale vidím tam funkci s nesmyslným názvem area(), která je vložena do nějakého rodiče obou objektů. Něco podobného dělají v PHP traits. Ovšem už ze zápisu té funkce vidím, že u čtverce zbytečně počítá cos(skew) - je vždy roven jedné. Raději napíši do každé třídy jiný vzoreček pro výpočet obsahu, než abych do třídy Čtverec dával atribut skew, který tam sémanticky vůbec nepatří. Místo jednoho atributu tam máš tři atributy - z toho dva jsou zcela zbytečné. Čtverec má jen jeden atribut a tím je délka strany.
interface Areable {
Double area()
}
struct Parallelograma : Areable {
var side1: Double
var side2: Double
var skew: Double
init(side1 s1: Double, side2 s2: Double, skew s: Double) {
side1 = s1
side2 = s2
skew = s
}
func area() -> Double {
return side1 * side2 * cos(skew)
}
}
struct Square : Areable {
var side: Double
init(side s1: Double) {
side = s1
}
func area() -> Double {
return side * side
}
}
let figure1 = Parallelogram(side1: 2, side2: 3, skew: M_PI_4)
let figure2 = Square(side: 3)
var list:Array<Areable> = [ figure1, figure2 ]
print(list.map { $0.area() })
Jak vidíš, zápis je ještě kratší, ještě jednodušší a pro mnohé i čitelnější.
1) Co je na area nesmyslného? Možná se douč AJ.
2) Omyl, u čtverce je jen jeden atribut, zbytek jsou computed properties. To nejsou atributy. Metoda pro výpočet obsahu se dá dopsat, v tomto případě tu generickou optimalizuje standardní knihovna (v obecném případě je jasné, že může dělat úplně něco jiného). Prostě toto řešení právě redukuje počet atributů, které nemá smysl mít v deklaraci (a tedy paměti).
-
Tento jazyk neznám, ale vidím tam funkci s nesmyslným názvem area(), která je vložena do nějakého rodiče obou objektů. Něco podobného dělají v PHP traits. Ovšem už ze zápisu té funkce vidím, že u čtverce zbytečně počítá cos(skew) - je vždy roven jedné.
To je právě ta pointa. Ta funkce spočítá plochu jakéhokoliv rovnoběžníka a její kód je tam jen jednou. Pro něco tak malého to nevynikne, ale pro větší kusy kódu je to jednoznačné plus. Ta nabušenost tohohle přístupu je evidentní třeba u STL v C++.
Raději napíši do každé třídy jiný vzoreček pro výpočet obsahu, než abych do třídy Čtverec dával atribut skew, který tam sémanticky vůbec nepatří. Místo jednoho atributu tam máš tři atributy - z toho dva jsou zcela zbytečné. Čtverec má jen jeden atribut a tím je délka strany.
Ale čtverec má i délku druhé strany a skew. Akorát jsou strany stejně dlouhé a skew je vždycky 0. Touhle úpravou jsi přišel o možnost použít čtverec kdekoliv, kde se dá použít obdélník nebo rovnoběžník. Tvoje verze zvládne jen ten obsah a ne všem ostatním si vyláme zuby.
Jak vidíš, zápis je ještě kratší, ještě jednodušší a pro mnohé i čitelnější.
Akorát se ztratilo veškeré sdílení kódu, což bylo to hlavní, co ten příklad ilustroval. Místo jednoho kusu kódu si ho musím u každé třídy implementovat znova a znova.
-
1) Co je na area nesmyslného? Možná se douč AJ.
Název area() se nehodí pro název metody, protože je to podstatné jméno. Je však možné ho použít pro vlastnost Area, která by se tu docela hodila. Možná se douč pravidla pojmenování tříd, metod a vlastností.
2) Omyl, u čtverce je jen jeden atribut, zbytek jsou computed properties. To nejsou atributy. Metoda pro výpočet obsahu se dá dopsat, v tomto případě tu generickou optimalizuje standardní knihovna (v obecném případě je jasné, že může dělat úplně něco jiného). Prostě toto řešení právě redukuje počet atributů, které nemá smysl mít v deklaraci (a tedy paměti).
Jsou tam tři privátní atributy: side1, side2 a skew. Snad ještě umím počítat. Všechny tři se nachází uvnitř objektu, ale to není podstatné, stejně jako nejsou podstatné optimalizace kompilátoru. Podstatné je, že čtverec má z definice jen jednu délku strany a žádné zkosení. Ve tvém řešení silně trpí sémantika jenom proto, že se pokoušíš sdílet nějakou metodu, která ani sdílena být nemá.
-
1) Co je na area nesmyslného? Možná se douč AJ.
Název area() se nehodí pro název metody, protože je to podstatné jméno. Je však možné ho použít pro vlastnost Area, která by se tu docela hodila. Možná se douč pravidla pojmenování tříd, metod a vlastností.
2) Omyl, u čtverce je jen jeden atribut, zbytek jsou computed properties. To nejsou atributy. Metoda pro výpočet obsahu se dá dopsat, v tomto případě tu generickou optimalizuje standardní knihovna (v obecném případě je jasné, že může dělat úplně něco jiného). Prostě toto řešení právě redukuje počet atributů, které nemá smysl mít v deklaraci (a tedy paměti).
Jsou tam tři privátní atributy: side1, side2 a skew. Snad ještě umím počítat. Všechny tři se nachází uvnitř objektu, ale to není podstatné, stejně jako nejsou podstatné optimalizace kompilátoru. Podstatné je, že čtverec má z definice jen jednu délku strany a žádné zkosení. Ve tvém řešení silně trpí sémantika jenom proto, že se pokoušíš sdílet nějakou metodu, která ani sdílena být nemá.
1) Přečti si konvence pojmenování metod ve Swiftu, než začneš kritizovat.
2) Je tam jeden atribut a dvě počítané vlastnosti, což je něco jako převlečená metoda. Zbytek ti vysvětlil JSH.
-
Záleží na aplikaci, když to bude geometrický tvar v počítačové hře, tak s immutable moc nepochodíš, tvar a poloha objektů se mění neustále.
Tohle je zajímavá otázka. Je lepší mít mutable scénu, upravovat ji a řešit synchronizaci? Nebo je lepší immutable scéna, update vytvoří novou, jen zrecykluje všechno co se nezměnilo?
Já vidím to immutable řešení jako podstatně elegantnější a i celkem efektivní. A není to jen moje teoretizování. Nenašel jsem přednášku kohosi od Epicu, kde jsem to slyšel poprvé, ale něco podobného zaznělo i tu https://www.youtube.com/watch?v=1PhArSujR_A (https://www.youtube.com/watch?v=1PhArSujR_A).
Tusim autor hry Iconclad :Steam Legions (psano v Clojure) se v blogu tvaril, jak se mu s immutable stavem hry strasne dobre pracovalo.
-
Jak vidíš, zápis je ještě kratší, ještě jednodušší a pro mnohé i čitelnější.
Akorát se ztratilo veškeré sdílení kódu, což bylo to hlavní, co ten příklad ilustroval. Místo jednoho kusu kódu si ho musím u každé třídy implementovat znova a znova.
To bylo mým úmyslem. Demonstrovat, že sdílení kousíčku kódu komplikovanými nástroji je složitější a podstatně méně čitelné, než použití interface. Pokud budu někde chtít použít třídu Čtverec, budu se oprávněně ptát, k čemu má tři atributy, když mi stačí jeden. Pokud to bude složitější výpočet, než jen obsah, mohu ten vzoreček sdílet v nějaké statické třídě - podobně jako je sestavena celá třída Math.
Program musí být v prvé řadě snadno čitelný člověkem programátorem. Kompilátor je až daleko za ním. Pokud je metoda pro výpočet obsahu zapouzdřena uvnitř svého objektu, je to nejen přehledné, ale i rychlé.
1) Přečti si konvence pojmenování metod ve Swiftu, než začneš kritizovat.
Zrovna pojmenování metod tam chybí. Přitom běžně se i ve Swiftu dodržuje pravidlo, že název metody má začínat slovesem.
2) Je tam jeden atribut a dvě počítané vlastnosti, což je něco jako převlečená metoda. Zbytek ti vysvětlil JSH.
Ty dvě počítané vlastnosti jsou tam zcela zbytečné. A co je zbytečné, musí pryč.
-
Tohle je zajímavá otázka. Je lepší mít mutable scénu, upravovat ji a řešit synchronizaci? Nebo je lepší immutable scéna, update vytvoří novou, jen zrecykluje všechno co se nezměnilo?
Já vidím to immutable řešení jako podstatně elegantnější a i celkem efektivní. A není to jen moje teoretizování. Nenašel jsem přednášku kohosi od Epicu, kde jsem to slyšel poprvé, ale něco podobného zaznělo i tu https://www.youtube.com/watch?v=1PhArSujR_A (https://www.youtube.com/watch?v=1PhArSujR_A).
Immutable objekty fungují hezky v teorii, ale prakticky narazíš na to, že stejně musíš řešit synchronizaci. Dělám třeba hru, kde panáci sbírají pomeranče. Panák přijde k pomerančí ležícímu na zemi a strčí si ho do kapsy. Pomeranč je immutable. Takže co panák udělá, vytvoři novou kopii pomeranče, která je v jeho kapse. Starý pomeranč zruší. Doteď všechno v pohodě. A teď si řeknu, no jo, když mám všechno immutable a mám spoustu jader CPU, tak pustím všechny panáky paralelně a všechno bude mnohem rychlejší. Jenomže pak přijdou k jednomu pomeranči paralelně dva různí panáci. Oba ve stejný čas vytvoří kopii pomeranče ležícího na zemi a strčí si ho do kapsy. Starý pomeranč zruší. Všechno krásně funguje, akorát místo jednoho pomeranče mám dva. To není úplně přesně to, co bych chtěl.
S celou immutable scénou si to už vůbec nedovedu představit. Mám scénu na které je 50 panáků. Každý panák něco udělá a vytvoří si kopii scény. Mám 50 různých scén, každá je jiná. Co s tím budu dělat, jak dám 50 různých scén nějak deterministicky dohromady do jedné?
-
Tohle mi přijde jakože s immutable objekty nemáš moc praktických zkušeností - z jazyků, kde se používají hodně. Protože právě immutable data nemůžou být nekonzistentní z principu, tímhle vůbec není potřeba se zabývat, tenhle problém vůbec neexistuje.
Zkušenosti mám, například když spustíš dvě vlákna a každému předáš immutable kopii tvých dat, tak po skončení vláken musíš psát extra algoritmus na sloučení výsledků. U klasické synchronizované mutable struktury tohle neřešíš.
Samozřejmě má immutable přístup svoje výhody, ale není to univerzální všelék, jsou případy kdy je to kontraproduktivní.
-
Jak vidíš, zápis je ještě kratší, ještě jednodušší a pro mnohé i čitelnější.
Akorát se ztratilo veškeré sdílení kódu, což bylo to hlavní, co ten příklad ilustroval. Místo jednoho kusu kódu si ho musím u každé třídy implementovat znova a znova.
To bylo mým úmyslem. Demonstrovat, že sdílení kousíčku kódu komplikovanými nástroji je složitější a podstatně méně čitelné, než použití interface. Pokud budu někde chtít použít třídu Čtverec, budu se oprávněně ptát, k čemu má tři atributy, když mi stačí jeden. Pokud to bude složitější výpočet, než jen obsah, mohu ten vzoreček sdílet v nějaké statické třídě - podobně jako je sestavena celá třída Math.
Program musí být v prvé řadě snadno čitelný člověkem programátorem. Kompilátor je až daleko za ním. Pokud je metoda pro výpočet obsahu zapouzdřena uvnitř svého objektu, je to nejen přehledné, ale i rychlé.
1) Přečti si konvence pojmenování metod ve Swiftu, než začneš kritizovat.
Zrovna pojmenování metod tam chybí. Přitom běžně se i ve Swiftu dodržuje pravidlo, že název metody má začínat slovesem.
2) Je tam jeden atribut a dvě počítané vlastnosti, což je něco jako převlečená metoda. Zbytek ti vysvětlil JSH.
Ty dvě počítané vlastnosti jsou tam zcela zbytečné. A co je zbytečné, musí pryč.
1) Slovesem začíná, pokud nic nevrací.
2) Působíš dojmem začínajícího studenta, který ještě nechápe (nezná) souvislosti. JSH ti už vše kolem toho příkladu vysvětlil, stačí si číst jeho odpověď stále dokola ;)
-
Ty dvě počítané vlastnosti jsou tam zcela zbytečné. A co je zbytečné, musí pryč.
Ty počítané vlastnosti rozhodně nejsou zbytečné. Slouží k tomu aby šel čtverec použít všude tam, kde se dá použít rovnoběžník. Pokud mám třeba funkci na kreslení rovnoběžníka, tak pokud má čtverec tyhle dvě "zbytečné" vlastnosti tak ho tímhle způsobem můžu nakreslit taky.
Nebo chci přidat funkci pro obvod. Ve tvém případě musím tu funkci dodat do interface a doimplementovat ve všech odvozených třídách. Není jednodušší napsat jedinou funkci, která spočítá obvod rovnoběžníka a v případě čtverce využívá ty "zbytečné" počítané vlastnosti?
-
Panák přijde k pomerančí ležícímu na zemi a strčí si ho do kapsy. Pomeranč je immutable. Takže co panák udělá, vytvoři novou kopii pomeranče, která je v jeho kapse.
Ne. Vytvoříš novou kopii kapsy s pomerančem a novou kopii scény bez pomeranče. Už jsi někdy viděl panáky, kteří by vyráběli pomeranče? Zato si panák může vyměnit prázdnou kapsu (resp. měšec) za kapsu s pomerančem.
-
Zkušenosti mám, například když spustíš dvě vlákna a každému předáš immutable kopii tvých dat, tak po skončení vláken musíš psát extra algoritmus na sloučení výsledků. U klasické synchronizované mutable struktury tohle neřešíš.
Samozřejmě má immutable přístup svoje výhody, ale není to univerzální všelék, jsou případy kdy je to kontraproduktivní.
Nechat dvě vlákna upravovat jedny data má cenu jen pokud je ta pravděpodobnost kolize hodně malá. V takovém případě immutable data pomáhají. Upravující vlákno na konci jen zkontroluje ukazatel na stará data jestli se nezměnil a v případě kolize spustí výpočet znova.
Mutable struktura je buď synchronizovaná celá a pak to efektivně jede na jedno vlákno, nebo jsou synchronizované kousky. Pak jedno vlákno mění strukturu tomu druhému pod rukama. Nízkoúrovňově je ta struktura konzistentní ale na vyšší úrovni je to past vedle pasti.
-
Poněkud názornější (a užitečnější) příklad protocol-based programming:
protocol Differentiable {
var derivative:Differentiable { get }
}
extension Double : Differentiable {
var derivative:Differentiable { return 0 }
}
struct Variable : Differentiable, CustomStringConvertible {
let name:String
init(name:String) { self.name = name; }
var derivative:Differentiable { return 1 }
var description:String { return name }
}
struct Sum : Differentiable, CustomStringConvertible {
let lhs:Differentiable
let rhs:Differentiable
init(lhs:Differentiable, rhs:Differentiable) { self.lhs = lhs; self.rhs = rhs }
var derivative:Differentiable { return lhs.derivative + rhs.derivative }
var description:String { return "\(lhs) + \(rhs)" }
}
func +(lhs:Differentiable, rhs:Differentiable) -> Differentiable {
return Sum(lhs: lhs, rhs: rhs)
}
struct Multiplication : Differentiable, CustomStringConvertible {
let lhs:Differentiable
let rhs:Differentiable
init(lhs:Differentiable, rhs:Differentiable) { self.lhs = lhs; self.rhs = rhs }
var derivative:Differentiable { return lhs.derivative * rhs + lhs * rhs.derivative }
var description:String { return "\(lhs) * \(rhs)" }
}
func *(lhs:Differentiable, rhs:Differentiable) -> Differentiable {
return Multiplication(lhs: lhs, rhs: rhs)
}
let f = 2 * Variable(name: "x")
print("(\(f))' = \(f.derivative)")
-
Panák přijde k pomerančí ležícímu na zemi a strčí si ho do kapsy. Pomeranč je immutable. Takže co panák udělá, vytvoři novou kopii pomeranče, která je v jeho kapse.
Ne. Vytvoříš novou kopii kapsy s pomerančem a novou kopii scény bez pomeranče. Už jsi někdy viděl panáky, kteří by vyráběli pomeranče? Zato si panák může vyměnit prázdnou kapsu (resp. měšec) za kapsu s pomerančem.
A co takhle : Z panáka vypadne požadavek na zvednutí pomeranče. Update funkce scény zpracuje starou scénu a frontu požadavků a rozsoudí kolize. V nové verzi scény bude mít jeden panák pomeranč a druhý nějaký signál o tom že požadavek neprošel. Ta fronta požadavků může být fronta upravujících funkcí, akorát je spouští někdo jiný než sám panák. Dává to smysl?
-
Ne. Vytvoříš novou kopii kapsy s pomerančem a novou kopii scény bez pomeranče. Už jsi někdy viděl panáky, kteří by vyráběli pomeranče? Zato si panák může vyměnit prázdnou kapsu (resp. měšec) za kapsu s pomerančem.
Nepochopil jsi pointu. Je úplně jedno, jestli kopii pomeranče vytvoří panák nebo replikátor ze star treku. Ta kopie vznikne. Ve chvíli, kdy to paralelizuješ, můžou z jednoho pomeranče vzniknout dva, což nechceš, tohle se v reálném světě neděje. Problém je i s více různými scénami, taky nevíš, jak je dát dohromady.
-
Tusim autor hry Iconclad :Steam Legions (psano v Clojure) se v blogu tvaril, jak se mu s immutable stavem hry strasne dobre pracovalo.
Pokud někoho immutables ve hrách zajímá, mohlo by ho zajímat i tohle: https://www.youtube.com/watch?v=yIlDBPiMb0o (webová hra, jednoduchá, ale na videu celá vysvětlená)
Immutable objekty fungují hezky v teorii, ale prakticky narazíš na to, že stejně musíš řešit synchronizaci. Dělám třeba hru, kde panáci sbírají pomeranče. Panák přijde k pomerančí ležícímu na zemi a strčí si ho do kapsy. Pomeranč je immutable. Takže co panák udělá, vytvoři novou kopii pomeranče, která je v jeho kapse. Starý pomeranč zruší. Doteď všechno v pohodě.
No jo, jenže to asi mluvíš o nějakém jazyku, který má immutabilitu jenom na okrasu, nebo nevím. Protože vůbec nevím, proč by se něco někam mělo kopírovat, minimálně pokud scéna i panáček běží v jednom procesu.
Např. v Elixiru/Erlangu pokud mám trojici definující stav světa {panacci,prostredi,nastaveni} a chci zmenit jenom panacky, takze vytvorim novou trojici {panacci2,prostredi,nastaveni}, tak se s prostredi a nastaveni nedela vubec nic, interne se vezmou dva pointery a jenom ty se prekopiruji do nove trojice. A prekopirovani pointeru nebo zmena property snad neni rozdíl, nad kterým by bylo potřeba nějak zvlášť dumat. Čili žádné "starý pomeranč zruší", ale "pointer na pomeranč se smaže z prostředí a vloží panáčkovi do kapsy".
A teď si řeknu, no jo, když mám všechno immutable a mám spoustu jader CPU, tak pustím všechny panáky paralelně a všechno bude mnohem rychlejší. Jenomže pak přijdou k jednomu pomeranči paralelně dva různí panáci. Oba ve stejný čas vytvoří kopii pomeranče ležícího na zemi a strčí si ho do kapsy. Starý pomeranč zruší. Všechno krásně funguje, akorát místo jednoho pomeranče mám dva. To není úplně přesně to, co bych chtěl.
S celou immutable scénou si to už vůbec nedovedu představit. Mám scénu na které je 50 panáků. Každý panák něco udělá a vytvoří si kopii scény. Mám 50 různých scén, každá je jiná. Co s tím budu dělat, jak dám 50 různých scén nějak deterministicky dohromady do jedné?
Opět - to mluvíš asi o nějakém jazyku, který obsahuje sdílená data. Tímpádem potřebuješ synchoronizaci. Tímpádem můžeš úplně s klidem zůstat u klasického OOP, seš tam, kdes byl.
Znovu pro příklad: v Erlangu bys měl třeba prostředí a každého z panáčků jako samostatný proces a ty procesy by si posílaly zprávy. Nebo bys mohl mít jako samostatný proces každé políčko herního plánu a tímpádem by mohlo docházet i k paralelním změnám v různých místech plánu.
Zkušenosti mám, například když spustíš dvě vlákna a každému předáš immutable kopii tvých dat, tak po skončení vláken musíš psát extra algoritmus na sloučení výsledků. U klasické synchronizované mutable struktury tohle neřešíš.
Samozřejmě má immutable přístup svoje výhody, ale není to univerzální všelék, jsou případy kdy je to kontraproduktivní.
Tohle je stejnej případ, jako jsem teď psal kolegovi - tohle se v jazycích, které OPRAVDU immutable používají (tj. nemají ho jenom na okrasu) prostě neděje. Pokud máš jedna data a nad nimi chceš spustit deset různých operací, tak to je klasická SIMD situace a používá se na to map+reduce.
Spouštět x modifikací téže struktury (např. toho herního plánu) je nesmysl. Pokud vím, že to budu chtít dělat, tak strukturu rozparceluju a události na jedné parcele se implicitně serializují.
-
Ta kopie vznikne. Ve chvíli, kdy to paralelizuješ, můžou z jednoho pomeranče vzniknout dva, což nechceš, tohle se v reálném světě neděje.
Nic takového se právě neděje. Smysl immutable dat je v tom, že jsou tím, čím jsou a právěže je NEMUSÍM kopírovat a nikdy nekopíruju - předávám je všude tak, jak jsou, protože vím, že je nikdo nemůže změnit, čili si s tím nelámu hlavu.
Naopak tenhle problém by vznikl u mutable dat - pokud to spustíš na víc jádrech, tak budou dvě různá vlákna přistupovat k tomu stejnému MUTABLE pomeranči, tak máš problém.
-
tak to je klasická SIMD situace a používá se na to map+reduce.
Sorry, překlep, mělo být "MISD".
-
Nic takového se právě neděje. Smysl immutable dat je v tom, že jsou tím, čím jsou a právěže je NEMUSÍM kopírovat a nikdy nekopíruju - předávám je všude tak, jak jsou, protože vím, že je nikdo nemůže změnit, čili si s tím nelámu hlavu.
Naopak tenhle problém by vznikl u mutable dat - pokud to spustíš na víc jádrech, tak budou dvě různá vlákna přistupovat k tomu stejnému MUTABLE pomeranči, tak máš problém.
Zkusím to jednoduše. Mám pomeranč ve scéně. Chci ho přesunout z bodu A do bodu B. Pokud je immutable, musím vytvořit kopii. Buď pomeranče, nebo celé scény.
Vícevláknové zpracování funguje na immutable objektech výborně v teorii a může to fungovat výborně i v praxi, jenže ne v tomto konkrétním případě, protože se snažím o simulaci reálného světa a nemůžu si dovolit, aby mi jen tak samy od sebe vznikaly nové objekty.
-
Zkusím to jednoduše. Mám pomeranč ve scéně. Chci ho přesunout z bodu A do bodu B. Pokud je immutable, musím vytvořit kopii. Buď pomeranče, nebo celé scény.
Neděláš kopii ani jednoho. V "immutable jazycích" to funguje tak, že když se nic nemění, tak se na to nesahá. Když se něco mění, tak se to staré zničí a nové vytvoří. Takže to, co asi chceš říct, není KOPÍROVÁNÍ, ale náklady spojené se zničení a vytvořením nového.
Pokud myslíš tohle, tak to máš částečně pravdu, ale nesmíš si představovat, že ty náklady jsou v takových jazycích stejné jako třeba v Javě. Už jsem to tady psal x-krát v různých tématech: v Erlangu právě proto, že má jenom immutable struktury, má GC jistotu, že pointery jsou jenom "dozadu" (když mám struktury uspořádané podle času vzniku) a nemám tam žádné cykly. Čili můžu struktury rušit velice rychle a dělat různé jiné optimalizace, které bych jinak dělat nemohl. To je příklad jenom jednoho rozdílu, je jich víc. Druhý je ten, co jsem ti psal výš - zahození staré trojice neznamená zahození všech dat, ale jenom té části, která se změnila, ostatní zůstává pořád na místě a nedotčeno. Čili náklady, které s tím jsou spojené, velice záleží na tom, jak ta struktura vypadá. Některé jsou problematické, to je pravda (např. hluboký strom, kde často měním listy, způsobuje, že musím velké části stromu znovu zrekonstruovat).
-
Immutable objekty fungují hezky v teorii, ale prakticky narazíš na to, že stejně musíš řešit synchronizaci. Dělám třeba hru
...
Ja jsem taky chtel psat hru v Clojure a resil jsem uplne stejny problem (ale nedoresil). Bohuzel na netu neni dost zdrojaku, ktere by ukazovaly, jak to delat. Ale 100% vim, ze to lide vyresili a jde to (asi nejslavnejsi je John Carmack se svym prepisem Wolfensteina 3D do Haskellu). Takze bych doporucil, v dobrem, trochu vic trpelivosti, jsem si jisty, ze to neni tak obtizne.
-
To "vkládání" struktur není moc rozumné, někdy má podtřída (díky invariantům, viz výše) méně dat než rodič.
Eh? To je prave vyhoda Go, ze muzes mit polymorfismus (funkci) bez dedicnosti (dat). Ale pokud nekdo to vkladani struktur mit chce (jako ze se to casto hodi, treba prave v programovani GUI, z cehoz asi prameni uspech "klasickeho" OOP), tak ho muze pouzit.
-
Neděláš kopii ani jednoho. V "immutable jazycích" to funguje tak, že když se nic nemění, tak se na to nesahá. Když se něco mění, tak se to staré zničí a nové vytvoří. Takže to, co asi chceš říct, není KOPÍROVÁNÍ, ale náklady spojené se zničení a vytvořením nového.
Tohle je jen slovíčkaření. Když se něco mění, tak se to nově vytvoří. To je u mě kopie. Ty tomu kopie neříkáš, protože se to staré zničí.
Pokud myslíš tohle, tak to máš částečně pravdu, ale nesmíš si představovat, že ty náklady jsou v takových jazycích stejné jako třeba v Javě. Už jsem to tady psal x-krát v různých tématech: v Erlangu právě proto, že má jenom immutable struktury, má GC jistotu, že pointery jsou jenom "dozadu" (když mám struktury uspořádané podle času vzniku) a nemám tam žádné cykly. Čili můžu struktury rušit velice rychle a dělat různé jiné optimalizace, které bych jinak dělat nemohl. To je příklad jenom jednoho rozdílu, je jich víc. Druhý je ten, co jsem ti psal výš - zahození staré trojice neznamená zahození všech dat, ale jenom té části, která se změnila, ostatní zůstává pořád na místě a nedotčeno. Čili náklady, které s tím jsou spojené, velice záleží na tom, jak ta struktura vypadá. Některé jsou problematické, to je pravda (např. hluboký strom, kde často měním listy, způsobuje, že musím velké části stromu znovu zrekonstruovat).
Ano, teoreticky to funguje hezky a prakticky to taky funguje hezky, ale jen na úlohy, které pracují s daty, která jsou na sobě málo závislá a vlákna si je navzájem nemění. Taková telefonní ústředna nebo jabber server jsou ideální aplikace pro Erlang, každé vlákno si obsluhuje to svoje a vlákna si navzájem data nemění prakticky vůbec. S Erlangem ale narazíš, když se budeš pokoušet řešit úlohu opačného typu: velké množství vzájemně provázaných a interagujících objektů, žijících v jednom velkém společném světě.
-
Tohle je jen slovíčkaření. Když se něco mění, tak se to nově vytvoří. To je u mě kopie. Ty tomu kopie neříkáš, protože se to staré zničí.
Kopie znamená, že mám někde v paměti nějaká data a PŘEKOPÍRUJU je někam do nějaké jiné oblasti paměti - mám dvě KOPIE, které jsou stejné. A k tomuhle právě nikdy nedochází. Pokud jsou někde stejná data, tak se případně kopíruje jenom pointer na ně, protože vím, že je nikdo nikdy nezmění, takže můžu s klidem ten pointer předat komu chci.
(P.S. když mluvím o pointeru, tak mluvím o vnitřní implementaci, v jazyce jako takovém se pointery nepoužívají vůbec, není proč)
Ano, teoreticky to funguje hezky a prakticky to taky funguje hezky, ale jen na úlohy, které pracují s daty, která jsou na sobě málo závislá a vlákna si je navzájem nemění. Taková telefonní ústředna nebo jabber server jsou ideální aplikace pro Erlang, každé vlákno si obsluhuje to svoje a vlákna si navzájem data nemění prakticky vůbec. S Erlangem ale narazíš, když se budeš pokoušet řešit úlohu opačného typu: velké množství vzájemně provázaných a interagujících objektů, žijících v jednom velkém společném světě.
To jsou takové mlhavé statementy, založené kdovínačem. Telefonní ústředna snad není "provázaná"? Každý může komunikovat s každým a když komunikuje A s B, tak ani s jedním nemůže komunikovat nikdo jiný. Jestli tohle není provázanost, tak už fakt nevím :)
Nevím, kolik máš s FP zkušeností, ale mám trochu podezření (bez urážky!) že ty tvoje zkušenosti vychází z toho, že jsi vzal svoje znalosti z konvenčních jazyků, snažil ses to naroubovat na FP a ono to nefungovalo moc dobře. Nevím, v jakém jazyce a co jsi zkoušel napsat, ale takhle obecně se o tom dá mluvit dost těžko, musel by se vzít konkrétní kód a podívat se na něj.
-
Ja jsem taky chtel psat hru v Clojure a resil jsem uplne stejny problem (ale nedoresil). Bohuzel na netu neni dost zdrojaku, ktere by ukazovaly, jak to delat. Ale 100% vim, ze to lide vyresili a jde to (asi nejslavnejsi je John Carmack se svym prepisem Wolfensteina 3D do Haskellu). Takze bych doporucil, v dobrem, trochu vic trpelivosti, jsem si jisty, ze to neni tak obtizne.
Samozřejmě že jde napsat Wolfenstein 3D v Haskellu. Stejně jako jde napsat webserver v bashi. Fungovat to bude, ale je to spíš jen takové myšlenkové cvičení, prakticky se funkcionální jazyky ve vývoji her neprosazují.
-
Samozřejmě že jde napsat Wolfenstein 3D v Haskellu. Stejně jako jde napsat webserver v bashi. Fungovat to bude, ale je to spíš jen takové myšlenkové cvičení, prakticky se funkcionální jazyky ve vývoji her neprosazují.
Tak to se neprosazují v ničem, to není specialita her ;) A důvody k tomu můžou být různé - třeba to, že každý programátor začíná imperativně nebo objektově. Kdyby každý začínal funkcionálně, kdoví, jestli by se OOP "prosadilo" ;)
Každopádně se podívej na to video s Carmackem. Když ti Carmack řekne, že funkcionální přístup je fajn, tak asi to s těma hrama nebude tak tragicky nemožný :)
-
Kopie znamená, že mám někde v paměti nějaká data a PŘEKOPÍRUJU je někam do nějaké jiné oblasti paměti - mám dvě KOPIE, které jsou stejné. A k tomuhle právě nikdy nedochází. Pokud jsou někde stejná data, tak se případně kopíruje jenom pointer na ně, protože vím, že je nikdo nikdy nezmění, takže můžu s klidem ten pointer předat komu chci.
(P.S. když mluvím o pointeru, tak mluvím o vnitřní implementaci, v jazyce jako takovém se pointery nepoužívají vůbec, není proč)
Já ale neřeším interní implementaci, ta je mi ukradená a může být úplně jiná v každém jazyce. Máš immutable objekt, který nejde změnit. Ty s ním chceš něco udělat, takže musíš vytvořit nový objekt. To je u mě kopie. Říkej si tomu třeba kopírování pointerů, interně to tak klidně může fungovat, ale nesnaž se tvrdit, že jsou to ty stejné objekty. Nejsou. Interně můžou být, ale taky nemusí. Logicky je to jiný objekt.
To jsou takové mlhavé statementy, založené kdovínačem. Telefonní ústředna snad není "provázaná"? Každý může komunikovat s každým a když komunikuje A s B, tak ani s jedním nemůže komunikovat nikdo jiný. Jestli tohle není provázanost, tak už fakt nevím :)
Telefonní ústředna je z hlediska logiky funkce jednoduché zařízení, prakticky jen spojuje hovory, dá se to implementovat třeba relativně jednoduchým stavovým automatem, žádné složitosti v tom nehledej.
Nevím, kolik máš s FP zkušeností, ale mám trochu podezření (bez urážky!) že ty tvoje zkušenosti vychází z toho, že jsi vzal svoje znalosti z konvenčních jazyků, snažil ses to naroubovat na FP a ono to nefungovalo moc dobře. Nevím, v jakém jazyce a co jsi zkoušel napsat, ale takhle obecně se o tom dá mluvit dost těžko, musel by se vzít konkrétní kód a podívat se na něj.
Nevím kolik máš zkušeností s vývojem her, ale mám trochu podezření (bez urážky!), že ty tvoje zkušenosti vychází z toho, že jsi vzal svoje znalosti funkcionálních jazyků a snažíš se je naroubovat na simulaci reálného světa a ono to nefunguje moc dobře. Nevím, v jakém jazyce a co jsi zkoušel napsat, ale takhle obecně se o tom dá mluvit dost těžko, musel by se vzít konkrétní kód a podívat se na něj. ;)
-
Každopádně se podívej na to video s Carmackem. Když ti Carmack řekne, že funkcionální přístup je fajn, tak asi to s těma hrama nebude tak tragicky nemožný :)
Na to video se podívám, ale nemyslím si, že by mě Carmack přesvědčil :) Jinak aby to nevyznělo špatně, já mám funkcionální programování rád, ale mám problém s tím, když se snaží roubovat i na úlohy, na které se funkcionální přístup z principu nehodí.
-
Já ale neřeším interní implementaci, ta je mi ukradená a může být úplně jiná v každém jazyce. Máš immutable objekt, který nejde změnit. Ty s ním chceš něco udělat, takže musíš vytvořit nový objekt. To je u mě kopie. Říkej si tomu třeba kopírování pointerů, interně to tak klidně může fungovat, ale nesnaž se tvrdit, že jsou to ty stejné objekty. Nejsou. Interně můžou být, ale taky nemusí. Logicky je to jiný objekt.
Tak já nevím, možná mluvím Čínsky, ale vůbec nerozumím, proč tohle píšeš, protože to vůbec nesouvisí s tím, co jsem napsal. No, nechme to být, zavání to spíš zbytečným flejmem než zajímavou diskusí...
Telefonní ústředna je z hlediska logiky funkce jednoduché zařízení, prakticky jen spojuje hovory, dá se to implementovat třeba relativně jednoduchým stavovým automatem, žádné složitosti v tom nehledej.
Ugh. Viděl jsi někdy konfiguraci Asterisku? A viděl jsi někdy specifikaci Jabber protokolu a k tomu alespoň pár rozšíření? :)
Nevím kolik máš zkušeností s vývojem her
To je jednoduchý - žádný. Taky jsem o vývoji her nic netvrdil, mluvím jenom o FP a vyvracím tvoje tvrzení o něm.
-
Já ale neřeším interní implementaci, ta je mi ukradená a může být úplně jiná v každém jazyce. Máš immutable objekt, který nejde změnit. Ty s ním chceš něco udělat, takže musíš vytvořit nový objekt. To je u mě kopie. Říkej si tomu třeba kopírování pointerů, interně to tak klidně může fungovat, ale nesnaž se tvrdit, že jsou to ty stejné objekty. Nejsou. Interně můžou být, ale taky nemusí. Logicky je to jiný objekt.
Tak já nevím, možná mluvím Čínsky, ale vůbec nerozumím, proč tohle píšeš, protože to vůbec nesouvisí s tím, co jsem napsal. No, nechme to být, zavání to spíš zbytečným flejmem než zajímavou diskusí...
Je tahle "1" neco jineho, nez tahle "1"? Jak je odlisim? Hodnota je hodnota a je uplne jedno, jestli ji kopiruju nebo na ni nejak odkazuju. Nema identitu a nemuze se zmenit. Pokud postava bere pomeranc a oboji ma byt immutable, pak je vysledkem nova postava. Pokud jsou dve ruzne a jsou obe to soucasti jedne immutable sceny, tak je vysledkem jina immutable scena. Je to stejne, jako 1+1+1. Kdybych to mel paralelizovat, tak musim na konci resit synchronizaci, protoze vysledkem je jedna nova zmenena scena, odlisna od puvodni, jina hodnota. Ta synchronizace se postara, ze "vyhraje" jedna z postav, ale nemelo by dojit ke kolizi, protoze nema jak, pokud je to cele immutable.
-
Je tahle "1" neco jineho, nez tahle "1"? Jak je odlisim? Hodnota je hodnota a je uplne jedno, jestli ji kopiruju nebo na ni nejak odkazuju.
Neni to jedno, protoze odkaz je prakticky zadarmo, kopirovani muze byt hodne drahy. A jestli nekdo tvrdi, ze problem je v kopirovani, tak to je proste kravina, protoze se nic nekopiruje, jenom odkazuje.
Pokud postava bere pomeranc a oboji ma byt immutable, pak je vysledkem nova postava. Pokud jsou dve ruzne a jsou obe to soucasti jedne immutable sceny, tak je vysledkem jina immutable scena. Je to stejne, jako 1+1+1. Kdybych to mel paralelizovat, tak musim na konci resit synchronizaci, protoze vysledkem je jedna nova zmenena scena, odlisna od puvodni, jina hodnota. Ta synchronizace se postara, ze "vyhraje" jedna z postav, ale nemelo by dojit ke kolizi, protoze nema jak, pokud je to cele immutable.
Sorry, ale já vůbec nechápu, co řešíte. Jaký "Pokud jsou dve ruzne a jsou obe to soucasti jedne immutable sceny"? Mám prostě
def init_state do
...
end
def aktualizuj_postavu(postava) do
...
end
def aktualizuj_stav(stary_stav) do
{stara_postava,nejaky_zatracene_velky_data,jiny_zatracene_velky_data} = stary_stav
nova_postava = aktualizuj_postavu(stara_postava)
{nova_postava,nejaky_zatracene_velky_data,jiny_zatracene_velky_data}
end
def loop(stav) do
stav2 = aktualizuj_stav(stav)
loop(stav2)
end
def main do
stav1 = init_state
loop(stav1)
end
- nejaky_zatracene_velky_data se nikam nekopiruji. Jedine, co se zdestruuje a znovu vytvori v kazdem aktualizuj_stav, je jedna trojice ukazatelu. Stejne tak to funguje v aktualizuj_postavu. Nevidim zadny kopirovani velkych dat ani zadny problem se synchronizaci.
Jasne, jsou struktury, kde to dela problemy, protoze jsou moc hluboce vnorene a musi se hodne restaurovat. Ale to jsem prece napsal. Stejne tak muzu chtit paralelni zmeny stavu, ale to se resi partitioningem, to jsem taky napsal.
Fakt nevim, s čím máte pořád problém.
-
...a už vůbec nechápu to argumentaci vícevláknovým zpracováním. Když budu mít klasický přístup, řekněme objekt, a budu ho chtít modifikovat ze dvou vláken, tak udělám co? Zamču, upravím, odemču, zamču, upravím. Což můžu úplně klidně dělat i v immutable jazycích, prostě použiju actor.
-
Řekl bych, že to zmatení s immutable a víc vlákny je z tohoto pohledu:
Mám immutable data, řekněme číslo 5 někde, a mám dvě vlákna, které mají referenci na tu 5 (obě vlákna drží ukazatel na stejnou adresu), pokud obě provedou příčtení čísla 2 , tak dojde k copy on write, obě vlákna ukazují na číslo 7, ale na dvou místech (a pokud původní 5 už nikdo nedrží, likviduje ji GC). Gamer se ptá, jak udělat, aby když ty dvě vlákna to přičtení udělají, aby viděly výsledek 9....
V tomto případě musí probíhat synchronizace mezi vlákny a předávat si referenci na poslední platný stav. Např:
Mám hlavní vlákno, které modifikuje "scénu" s immutable daty 5, odkaz na tuto scénu předá těm dvěma vláknům. Pokud se má synchornizovaně přičíst každé ty 2, tak přičítací vlákna posílají zprávu držiteli scény, přičti 2, ten to udělá (a vznikne mu nové scéna prvně se 7 a pak hned s 9) a odkaz na nové scény předá vláknům, která pak vidí od okamžiku přijmu nového odkazu poslední stav scény (a jak postupně všechna vlákna opustí scény s daty 5 a 7, tak je GC likviduje).
-
Řekl bych, že to zmatení s immutable a víc vlákny je z tohoto pohledu:
Mám immutable data, řekněme číslo 5 někde, a mám dvě vlákna, které mají referenci na tu 5 (obě vlákna drží ukazatel na stejnou adresu), pokud obě provedou příčtení čísla 2 , tak dojde k copy on write, obě vlákna ukazují na číslo 7, ale na dvou místech (a pokud původní 5 už nikdo nedrží, likviduje ji GC). Gamer se ptá, jak udělat, aby když ty dvě vlákna to přičtení udělají, aby viděly výsledek 9....
Ano, já si taky myslím, že tak nějak ta gamerova představa je. Jenže tohle se prostě ve FP neděje, to je představa přenesená z imperativních jazyků. Ve FP se nepracuje s žádnými referencemi v tomhle smyslu. Pokud mám dvě funkce f1 a f2, jedna přičítá jedničku a druhá dvojku, a předhodím jím tentýž stav (5), tak mi prostě jedna vrátí 6 a druhá 7, to je správně a má to tak být. Jak jsem říkal, není to nic jiného než starý dobrý https://en.wikipedia.org/wiki/MISD Pokud chci ty funkce na stav aplikovat postupně, tak to opravdu musím udělat postupně - f1 mi vrátí 6 a potom aplikuju f2, která mi vrátí 8. V jazycích, které mají speciální vlastnosti (Haskell) si pak taky můžu hrát třeba s tím, že na pořadí aplikace nezáleží, můžu f1 a f2 snadno sloučit do jedné operace atd. atd. Mám tam úplně jiné možnosti a proto bych jich měl využívat a ne psát kód imperativním stylem a divit se, že mi to nefunguje podle představ...
Mimochodem, když mám immutabilní stav a ten MISD model, tak můžu dělat jednu krásnou věc: spekulativní výpočet. Čili něco, o čem si mutabilní přístup může nechat tak leda zdát :) Opět: je ale potřeba to umět využít a využívat...
...a pokud bych to skutečně chtěl udělat takhle, pomocí nějakého typu reference, tak třeba v Elixiru/Erlangu se na to právě používá actor (agent) - ten drží jedinou kopii dat a provádí nad němi dané operace. Takže obě vlákna drží nějakou referenci na actor (stačí jeho jméno) a posílají mu instrukce, jak má stav měnit. Actor to pak dělá ve vlastním vlákně, klidně asynchronně, původní vlákna dostanou zprávu, až je to hotovo, mezi tím můžou dělat něco jiného klidně.
Prostě možností, jak to celé uspořádat, je bambilion a připadá mi, že gamer se upnul na jednu představu, která ještě ke všemu moc neodpovídá realitě.
(A nebo se možná úplně pletu, gamer přesně ví, o čem mluví, a má pravdu. Pak by to ale chtělo líp vysvětlit pro nás méně chápavé.)
-
Code reuse samozřejmě neznamená jen dědičnost. Dědičnost je jedna z metod. A ten kdo umí dědičnost a neumí jiné metody bude obhajovat dědičnost, a kdo neumí dědičnost a umí jiné metody ji bude hanit. A oba se budou hádat, že "to druhé" je cesta do pekla. Dobrý programátor prostě použije vhodně daný kontext a max si zanadává - todle by šlo v támdletom jazyku napsat elegantnějc. Já takhle střídám několik jazyků a rozstu z toho dost.
Jazyk, který poskytuje takové nástroje, že je průměrný programátor nikdy nenaučí používat, je docela na pytel, nemyslíš? Bez ohledu na to, že si myslím, že dědičnost umím používat, tak jde o to, že spousta programátorů ne. Programátorské jazyky a metodiky nám mají pomáhat, ne nás filtrovat podle inteligence a zkušeností. To jsme si pak tak nějak spletli barák.
-
Neni to jedno, protoze odkaz je prakticky zadarmo, kopirovani muze byt hodne drahy. A jestli nekdo tvrdi, ze problem je v kopirovani, tak to je proste kravina, protoze se nic nekopiruje, jenom odkazuje
Spatne jsem se vyjadril a asi spatne citoval, byla to reakce hlavne na gamera. Ten rozdil je v implementaci, ale ne semanticky. Hodnota je hodnota - presne jako ve vasem kodu, "zmena" hodnoty proste vytvori novou hodnotu, hotovo (a ani ta se v praxi nemusi kopirovat cela, protoze mame persistent data structures). I se zbytkem souhlasim a vcetne te synchronizace pri paralelizaci jsem se snazil napsat to same. Actor taky neni nic jineho, nez zpusob synchronizace zmen stavu.
Vnorene struktury jsou trochu problem, ale na to zase pomuzou lenses.
-
byla to reakce hlavne na gamera.
Aha, tak to jsme si nerozuměli, já to chápal přesně opačně :)
P.S. nemohli bysme si tykat, mně to vykání vždycky přijde jakoby mi bylo padesát a děsí mě to ;)
Ten rozdil je v implementaci, ale ne semanticky. Hodnota je hodnota - presne jako ve vasem kodu, "zmena" hodnoty proste vytvori novou hodnotu, hotovo.
Já bych to asi polopaticky řekl takhle: celý to nepochopení je způsobený tím, že v imperativních jazycích se pracuje s pamětí - s chlívečky, které jsou nějak pojmenované a postupně do nic něco strkám. Tímpádem i dvě vlákna můžou vidět tentýž chlíveček a něco tam strčit.
U FP se nepracuje s chlívečky, ale s hodnotami. Hodnota je hodnota a nic se do ní nestrká, ani nikdo nemůže změnit 1 na 2 a ještě ke všemu tak, aby pak 1 bylo 2 i v sousedním vlákně. 1 je prostě 1 vždy a všude ;)
-
Jenže tohle se prostě ve FP neděje, to je představa přenesená z imperativních jazyků. Ve FP se nepracuje s žádnými referencemi v tomhle smyslu. Pokud mám dvě funkce f1 a f2, jedna přičítá jedničku a druhá dvojku, a předhodím jím tentýž stav (5), tak mi prostě jedna vrátí 6 a druhá 7, to je správně a má to tak být.
Však jo, já neříkám nic jiného, takhle to v FP funguje a má fungovat. A FP se taky zaklíná tím, že mávnutím kouzelného proutku řeší problémy s paralelizací a synchronizací, které mají imperativní jazyky. Řeší, ale nikoliv obecně, funguje to jen pro určité úlohy. Ve chvíli, kdy se jedná o nějaký složitejší problém (paralelním výpočtem vznikne více různých scén), tak nemám žádnou metodu, jak je dát deterministicky dohromady a stejně skončím na tom, že i v FP výpočet musím spustit buď v jednom vláknu, nebo řešit nějakou interní synchronizaci na úrovni jednotlivých objektů.
-
A FP se taky zaklíná tím, že mávnutím kouzelného proutku řeší problémy s paralelizací a synchronizací, které mají imperativní jazyky. Řeší, ale nikoliv obecně, funguje to jen pro určité úlohy.
Nu, prostředí s immutable daty neřeší vše, ale odstraňuje řadu synchrobnizačních problémů, které řeším nad muttable daty se zámky/bariérama a podobnými, aby si např. různá vláka nepřepisovala nahodile a neřízeně data. Tento přístup předchází i vzniku celé řady deadlockům z toho (ale ne všch) a zjednodušuje to.
Nicméně pro situace, kdy musí docházet k synchronizaci mezi různými částmi aplikace z podstaty problému co řeší, tak ji musím stejně definovaně řešit (jenom patříčnými prostředky daného prostředí, např ty zmíněné actory v Erlangu a hromadě dalších).
-
Však jo, já neříkám nic jiného, takhle to v FP funguje a má fungovat. A FP se taky zaklíná tím, že mávnutím kouzelného proutku řeší problémy s paralelizací a synchronizací, které mají imperativní jazyky. Řeší, ale nikoliv obecně, funguje to jen pro určité úlohy. Ve chvíli, kdy se jedná o nějaký složitejší problém (paralelním výpočtem vznikne více různých scén), tak nemám žádnou metodu, jak je dát deterministicky dohromady
Ale samozřejmě že máš, vždyť jsem ti napsal několik metod. Pokud je ale ta úloha z definice sekvenční, tak ji prostě FP žádným magickým proutkem samo neparalelizuje, to dá rozum. Pokud budeš počítat sha(md5(x)), tak na to prostě nemůžeš jít stylem y=md5(x) z=sha(x) a divit se, že neumíš y a z "dát deterministicky dohromady". Protože to prostě takhle nejde.
Jinak, asi nejlepší přístup, který se dá pro hry použít, je FRP. Viz
- https://youtu.be/1MNTerD8IuI?t=7m23s
- https://leanpub.com/gameinhaskell
Naštěstí vždycky, když něco nejde, tak se najde nějakej blbec, kterej neví, že to nejde, a naprogramuje to :)
- http://helm-engine.org/
- http://blog.chucklefish.org/wayward-tide-technical-details/
- https://www.youtube.com/watch?v=JleoASegUlk
- https://www.youtube.com/watch?v=0jYdu2u8gAU
-
Řekl bych, že to zmatení s immutable a víc vlákny je z tohoto pohledu:
Mám immutable data, řekněme číslo 5 někde, a mám dvě vlákna, které mají referenci na tu 5 (obě vlákna drží ukazatel na stejnou adresu), pokud obě provedou příčtení čísla 2 , tak dojde k copy on write, obě vlákna ukazují na číslo 7, ale na dvou místech (a pokud původní 5 už nikdo nedrží, likviduje ji GC). Gamer se ptá, jak udělat, aby když ty dvě vlákna to přičtení udělají, aby viděly výsledek 9....
Takové chování taky v některých FP jazycích dosáhneš, ale je tady jedno velké ALE: v těchto jazycích (dobře budu mluvit konkrétně o Clojure) se nepoužívají běžné proměnné, ale (zjednodušeně) speciální formy referencí na neměnná data. Takže máš například tzv. atomy, jejichž změna je z pohledu vlákna synchronní, tzv. agenty, kde je změna asynchronní (nemusí nastat hned) a refs, jejichž změnu (reference, ne hodnoty) je nutné udělat uvnitř transakce. Již jenom toto rozdělení dost dobře programátora donutí o řešeném algoritmu přemýšlet a ne semtam do kódu vrazit "synchronized" (což je zaprve pesimistická varianta, zadruhé DL čeká za nejbližším rohem :-)
-
Tusim autor hry Iconclad :Steam Legions (psano v Clojure) se v blogu tvaril, jak se mu s immutable stavem hry strasne dobre pracovalo.
Viz tez http://stackoverflow.com/questions/9755882/are-functional-programming-languages-suitable-for-graphics-programming (http://stackoverflow.com/questions/9755882/are-functional-programming-languages-suitable-for-graphics-programming).
-
Pokud je ale ta úloha z definice sekvenční, tak ji prostě FP žádným magickým proutkem samo neparalelizuje, to dá rozum. Pokud budeš počítat sha(md5(x)), tak na to prostě nemůžeš jít stylem y=md5(x) z=sha(x) a divit se, že neumíš y a z "dát deterministicky dohromady". Protože to prostě takhle nejde.
Ta úloha není z definice sekvenční, dá se bez problémů paralelizovat. Řeším třeba AI panáků, kteří sbírají pomeranče. Klidně můžu pustit každého panáka v samostantném vláknu, ale musím nějak synchronizovat přístup k pomerančům, protože dva panáci nesmí současně pracovat s jedním pomerančem. FP mi to nijak neusnadňuje, synchronizaci musí řešit taky, i když třeba jinými prostředky.
Naštěstí vždycky, když něco nejde, tak se najde nějakej blbec, kterej neví, že to nejde, a naprogramuje to :)
Nikdo netvrdí, že by to nešlo, samozřejmně že to jde. Ale je to neefektivní, imperativní přístup s mutable objetky dává lepší výsledky.
-
FP mi to nijak neusnadňuje, synchronizaci musí řešit taky, i když třeba jinými prostředky.
Musím řešit jen ten pomeranč, kterej jedinej je napříč vlákny. U mutable musím hlídat úplně všechno. To mi přijde jako dost velké usnadnění. (A to jsme ještě nevytáhly nástroje a zatím se bavíme jen o výhodách daných principy.)
Nikdo netvrdí, že by to nešlo, samozřejmně že to jde. Ale je to neefektivní, imperativní přístup s mutable objetky dává lepší výsledky.
Čím by si podložil své tvrzení? Nebo je to jen tvůj pocit?
-
Pokud je ale ta úloha z definice sekvenční
Ta úloha není z definice sekvenční, [...] dva panáci nesmí současně pracovat s jedním pomerančem.
Jsi si jistý? :))
Nikdo netvrdí, že by to nešlo, samozřejmně že to jde. Ale je to neefektivní, imperativní přístup s mutable objetky dává lepší výsledky.
Citation needed.
-
Ta úloha není z definice sekvenční, [...] dva panáci nesmí současně pracovat s jedním pomerančem.
Jsi si jistý? :))
Záleží na tom, jak se ta úloha uchopí. Pokud má panák kapsu (kolekci) a do ní strká pomeranče (položky), je při práci s takovou strukturou hromada rizik. Pokud však máme panáka bez kapsy a pomeranč, jehož atributem je majitel, problém se dvěma panáky najednou mizí. Atribut je jen jeden - pouze jeden panák může ten pomeranč vlastnit.
-
Pokud však máme panáka bez kapsy a pomeranč, jehož atributem je majitel, problém se dvěma panáky najednou mizí. Atribut je jen jeden - pouze jeden panák může ten pomeranč vlastnit.
...a jenom vlastník s ním může pracovat. Čili sekvenční kód, explicitně vylučující paralelismus ;)
-
Pokud však máme panáka bez kapsy a pomeranč, jehož atributem je majitel, problém se dvěma panáky najednou mizí. Atribut je jen jeden - pouze jeden panák může ten pomeranč vlastnit.
...a jenom vlastník s ním může pracovat. Čili sekvenční kód, explicitně vylučující paralelismus ;)
Proč? Akci panáka přece spustím prostřednictvím toho pomeranče. Pokud mám ve hře 10 pomerančů, mohu mít 10 paralelních větví.
Přesně tak to mám ve svém webovém RPG, ve kterém je držený meč vlastněn hráčem, batoh vlastněn hráčem a artefakt vlastněn batohem. Tím se mi většina vazeb zredukovala na 1:1. Předměty v batohu se neúčastní soubojů, štít chrání hráče pouze pokud je nasazen, hráč může být zaklet do artefaktu a uložen do batohu jiného hráče... Také databázi se ulevilo.
-
Proč? Akci panáka přece spustím prostřednictvím toho pomeranče. Pokud mám ve hře 10 pomerančů, mohu mít 10 paralelních větví.
No jasně, to je ten partitioning, o kterým jsem psal. Gamer je ale zhrzenej, že není paralelizovaná práce s jedním pomerančem a zároveň chce, aby k němu nemohli přistupovat dva hráči zároveň.
Prostě, když mám nějaký sdílený stav, tak přístup k němu musím vždycky serializovat, z principu, v tom mi žádná magická hůlka nepomůže. Výhoda FP je v tom, že množství sdílených stavů udržuje na nezbytném minimu a proto se dá paralelismus použít na víc místech než v kódu, kde je sdílené kde co a ani nemůžu efektivně zjistit, co je a co není. V tom je ta výhoda. Gamer ji hledá někde, kde není a být nemůže.
-
Musím řešit jen ten pomeranč, kterej jedinej je napříč vlákny. U mutable musím hlídat úplně všechno. To mi přijde jako dost velké usnadnění. (A to jsme ještě nevytáhly nástroje a zatím se bavíme jen o výhodách daných principy.)
Nemusím hlídat všechno, u mutable taky řeším jen ten pomeranč, který jde napříč vlákny. Nic jiného synchronizovat nemusím. Je to úplně stejné.
Čím by si podložil své tvrzení? Nebo je to jen tvůj pocit?
Težko srovnávat, protože velké funkcionální hry neexistují. Ani žádný významný herní engine není funkcionální. Existují jen menší hry, napsané jako hobby projekty. Ale budiž, musíme si vystačit s tím. Můžeš začít třeba tady:
http://stackoverflow.com/questions/9755882/are-functional-programming-languages-suitable-for-graphics-programming
Autor Ironclad: Steam Legions popisuje svoje zkušenosti, vytrhnu z kontextu jen tohle:
You need to be careful about performance
Mutability is useful in games
Zajímavé věci se dají dočíst i tady: http://prog21.dadgum.com/23.html
Je to od člověka, který udělal funkcionálního Pac-Mana. I když se jedná o velmi jednoduchou hru, byl donucen k nedobrým řešením. Protože nemůže rozumně během výpočtu měnit herní scénu, řeší změny scény až ex-post na základě eventů ({ate_dot,Coordinates}, ate_fruit). To je problém, protože může vygenerovat eventy, které jsou vzájemně v kolizi. Ex-post potom musíš řešit, co dělat, když jdou nějaké eventy proti sobě. U Pac-Mana s pár eventy to ještě asi bude řešitelné, ale nedokážu si to představit u větší hry, kde budou tisíce různých eventů, které můžou vznikat ve všech možných kombinacích. Dá se to vyřešit tak, že budu seznam už existujících eventů předávat do každé funkce a na základě toho pak rekonstruovat aktuální scénu, ale to je už úplně uhozené.
Můžeš argumentovat, že je to vlastně super, všechny výpočty dělám s konstatní scénou, která se nemění, takže to můžu perfektně paralelizovat. Jenomže imperativně můžu udělat to samé, můžu to paralelizovat úplně bez sychronizace, pokud neměním scénu. Prakticky to takhle ale nikdo nedělá, i když může. Není to dobrý přístup.
Pak ještě napsal takové obecné shrnutí o funkcionálním programování: http://prog21.dadgum.com/54.html
-
Proč? Akci panáka přece spustím prostřednictvím toho pomeranče. Pokud mám ve hře 10 pomerančů, mohu mít 10 paralelních větví.
Můžeš to tak udělat, ale není to dobrý design. O tom, co se stane s pomerančem, rozhoduje panák, pomeranč je jen pasivní objekt. Panák by měl tu akci provádět.
-
Gamer je ale zhrzenej, že není paralelizovaná práce s jedním pomerančem a zároveň chce, aby k němu nemohli přistupovat dva hráči zároveň.
Nevím, jestli nerozumíš tomu, co píšu, nebo jestli to schválně překrucuješ tak, jak se ti to hodí. Spíš bych tipoval to druhé, ale budiž, pokusím se to vysvětlit znovu.
Nechci paralelizovat práci s jedním pomerančem. Chci paralelizovat AI panáků. Ve hře mám 50 panáků a každý se musí rozhodnout, co bude dělat. Musí nejdřív zjistit, jaké vidí pomeranče a okolní panáky. Potom k těm pomeračům musí najít nejkratší cestu. Mohl by se taky snažit najít nejkratší cestu ostatních panáků k pomeračům. Na základě všech těchto informací se bude každý panák snažit najít optimální řešení, které bude maximalizovat jeho zisk - získá co nejvíc pomeranců. Můžu to pustit paralelně v samostatném vlákně po každého panáka. A já se ptám, jak mi pomůže, když to udělám funkcionálně? Bez sychronizace se stejně neobejdu, musím řešit co se stane, když panáci přistupují ke sdíleným pomerančům a taky musím řešit jejich kolize, aby se mi na cestě nesráželi.
Výhoda FP je v tom, že množství sdílených stavů udržuje na nezbytném minimu a proto se dá paralelismus použít na víc místech než v kódu, kde je sdílené kde co a ani nemůžu efektivně zjistit, co je a co není. V tom je ta výhoda. Gamer ji hledá někde, kde není a být nemůže.
To jsou jen takové teoretické řeči. I když to udělám funcionálně, nevypadne z toho nějak magicky, které zdroje jsou sdílené a které ne. Funcionální kód mi neřekne, že modří panáci sbírají jen pomeranče a červení panáci sbírají jen citróny. To je informace, kterou musím explicitně znát.
-
Zajímavé věci se dají dočíst i tady: http://prog21.dadgum.com/23.html
No, tak bysme měli jeden blog jednoho programátora, kterému to nešlo, fajn :)
Každopádně mám velké pochyby o tom, jak moc FP ten člověk zná, protože ten příklad s globálním proměnnou (např. skóre) je přesně to, na co se používá Actor/Agent - k zapouzdření sdíleného stavu a serializaci přístupu k němu. A používá se to velice snadno. Takže těžko říct, co vyvozovat z blogu, který říká, že to nejde...
http://elixir-lang.org/docs/stable/elixir/Agent.html
-
Nechci paralelizovat práci s jedním pomerančem. Chci paralelizovat AI panáků. Ve hře mám 50 panáků a každý se musí rozhodnout, co bude dělat. Musí nejdřív zjistit, jaké vidí pomeranče a okolní panáky. Potom k těm pomeračům musí najít nejkratší cestu. Mohl by se taky snažit najít nejkratší cestu ostatních panáků k pomeračům. Na základě všech těchto informací se bude každý panák snažit najít optimální řešení, které bude maximalizovat jeho zisk - získá co nejvíc pomeranců. Můžu to pustit paralelně v samostatném vlákně po každého panáka. A já se ptám, jak mi pomůže, když to udělám funkcionálně? Bez sychronizace se stejně neobejdu, musím řešit co se stane, když panáci přistupují ke sdíleným pomerančům a taky musím řešit jejich kolize, aby se mi na cestě nesráželi.
Napsal jsem ti několikrát několik způsobů, jak se taková věc řeší. Nebudu to opakovat, nemám důvod tě o něčem přesvědčovat, když o to nemáš zájem.
To jsou jen takové teoretické řeči. I když to udělám funcionálně, nevypadne z toho nějak magicky, které zdroje jsou sdílené a které ne.
Ale samozřejmě, že vypadne, protože ve FP prostě žádný sdílený stav z principu být nemůže. Prostě to nejde, není, na úrovni kódu neexistuje. A pokud ho potřebuješ na úrovni sémantiky, tak se to dělá agentem, čili máš ten stav na jednom místě, jasně zapouzdřený a s jasným API.
Tím bych s dovolením tuhle debatu ukončil, fakt to nikam nevede, myslel jsem, že by to mohla být zajímavá debata, ale dostali jsme se do stádia přesvědčování a v tom nemá smysl pokračovat.
-
Proč? Akci panáka přece spustím prostřednictvím toho pomeranče. Pokud mám ve hře 10 pomerančů, mohu mít 10 paralelních větví.
Můžeš to tak udělat, ale není to dobrý design. O tom, co se stane s pomerančem, rozhoduje panák, pomeranč je jen pasivní objekt. Panák by měl tu akci provádět.
Panák může říct pomeranči: Teď máš nového panáka, tak si to zapiš za uši.
-
http://prog21.dadgum.com/23.html
soudě podle toho co píše, autor blogu je tupec
-
Samozřejmě že jde napsat Wolfenstein 3D v Haskellu. Stejně jako jde napsat webserver v bashi. Fungovat to bude, ale je to spíš jen takové myšlenkové cvičení, prakticky se funkcionální jazyky ve vývoji her neprosazují.
A důvody k tomu můžou být různé - třeba to, že každý programátor začíná imperativně nebo objektově.
Nie kazdy.
Zaciatok programovania na MIT je s Lispom (Scheme):
http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-001-structure-and-interpretation-of-computer-programs-spring-2005/video-lectures/
Myslim, ze ked to ma MIT, tak to budu mat aj dalsie univerzity.
S Haskellom zacina najmenej 13 univerzit vratane Oxfordu:
https://wiki.haskell.org/Haskell_in_education#Haskell_as_a_first_language
-
Zaciatok programovania na MIT je s Lispom (Scheme) [...] S Haskellom zacina najmenej 13 univerzit vratane Oxfordu:
Myslis, ze na MIT a Oxford chodi lidi, kteri nikdy v nicem neprogramovali? :)
-
Odpoviem protiotazkou: Myslis, ze na informaticke smery MFF/CVUT/VUT chodia ludia, co v nicom neprogramovali? (Ja myslim, ze ano, aj ked ich je mensina)
Inak tu je dalsich par, kde sa zacina funkcionalne:
http://www.quora.com/Which-universities-teach-their-first-CS-course-in-a-functional-programming-language
Je tam aj India, takze z pohladu mnozstva to ma sancu.
-
Ja myslim, ze ano, aj ked ich je mensina
Mas pravdu, opravuju to tvrzeni z "kazdy programator" na "vetsina programatoru" ;)
-
Zaciatok programovania na MIT je s Lispom (Scheme) [...] S Haskellom zacina najmenej 13 univerzit vratane Oxfordu:
Myslis, ze na MIT a Oxford chodi lidi, kteri nikdy v nicem neprogramovali? :)
Dodnes mě mrzí, že jsem nezačínal s Lispem, ale s Pascalem. O to krutější byl přechod na OOP.
-
Protože nemůže rozumně během výpočtu měnit herní scénu, řeší změny scény až ex-post na základě eventů ({ate_dot,Coordinates}, ate_fruit). To je problém, protože může vygenerovat eventy, které jsou vzájemně v kolizi. Ex-post potom musíš řešit, co dělat, když jdou nějaké eventy proti sobě.
No, já jsem to řešil tak, že jsem měl scénu, ta měla tiky, a v každém tiku jednotlivé objekty udělaly změny na scéně co potřebovali. Na konci tiku se vždycky uplatnili změny, a ty se zapsali zpět do scény. A tak se to opakovalo.
Pokud tam dva paňácové šahali po pomeranči, tak ten první ho dostal, a ten druhej šáhnul do prázdna (v příším tiku by se to dozvěděl). Jednoduše, kdo dřív přijde. Podobně to fungovalo když by se měl ten paňáca hejbat, a vznikla by nějaká kolize (narazil do jiného paňáci).
Jako fungovalo to parádnicky, a asi ani v mutable prostředí bych to nedělal jinak.
Je možné, že jsem si to moc zjednodušil a na nějaké über-extra-mega složité hře bych narazil, ale zatím si to nemyslím, a k negativním zkušenostem druhých jsem skeptický.
-
Musím řešit jen ten pomeranč, kterej jedinej je napříč vlákny. U mutable musím hlídat úplně všechno. To mi přijde jako dost velké usnadnění. (A to jsme ještě nevytáhly nástroje a zatím se bavíme jen o výhodách daných principy.)
Nemusím hlídat všechno, u mutable taky řeším jen ten pomeranč, který jde napříč vlákny. Nic jiného synchronizovat nemusím. Je to úplně stejné.
No musíš hlídat všechno. Protože všechny objekty máš mutable. To znamená nevíš dne a hodiny, kdy ti někdo nepřepíše. Klasickej problém:
a = getAccount
b = getBalance a
setBalance a b+1
ti v mutable a v immutable světě bude fungovat různě. Ale to snad nemusím vysvětlovat.
-
No, já jsem to řešil tak, že jsem měl scénu, ta měla tiky, a v každém tiku jednotlivé objekty udělaly změny na scéně co potřebovali.
Moment, tohle je imperativní přístup s mutable objekty, co mi uchází? Nebo jsi jako parametr do funkce předal celou scénu a z funkce dostal novou scénu?
-
No musíš hlídat všechno. Protože všechny objekty máš mutable. To znamená nevíš dne a hodiny, kdy ti někdo nepřepíše.
Ne nemusím hlídat všechno. Hlídám jen objekty sdílené mezi vlákny. Když sdílím pomeranče, hlídám pomeranče. Když nesdílím citrony, tak je ani nemusím hlídat.
-
Ne nemusím hlídat všechno. Hlídám jen objekty sdílené mezi vlákny. Když sdílím pomeranče, hlídám pomeranče. Když nesdílím citrony, tak je ani nemusím hlídat.
A musíš hlídat, že pomeranče nemají někde schovaný odkaz na citrony.
-
No, já jsem to řešil tak, že jsem měl scénu, ta měla tiky, a v každém tiku jednotlivé objekty udělaly změny na scéně co potřebovali.
Moment, tohle je imperativní přístup s mutable objekty, co mi uchází? Nebo jsi jako parametr do funkce předal celou scénu a z funkce dostal novou scénu?
No, ve skutečnosti jsem to dělal pomocí zástupných commandů, ale ten princip, ať si to nekomplikujem je ten, že jsem funkci předal celou scénu, a vrátil novou.
No musíš hlídat všechno. Protože všechny objekty máš mutable. To znamená nevíš dne a hodiny, kdy ti někdo nepřepíše.
Ne nemusím hlídat všechno. Hlídám jen objekty sdílené mezi vlákny. Když sdílím pomeranče, hlídám pomeranče. Když nesdílím citrony, tak je ani nemusím hlídat.
No dobře. Teoreticky nemusíš hlídat všechno. Prakticky, když jsem to nehlídal, tak jsem nedotáhl projekt do konce.
-
A musíš hlídat, že pomeranče nemají někde schovaný odkaz na citrony.
V tom případě sdílím i citrony. A FP mi nijak nepomůže, sychronizaci citronů musím i v FP řešit stejně jako sychronizaci pomerančů, pokud jde s citronem přes pomeranč provádět nějaká akce.
-
No, ve skutečnosti jsem to dělal pomocí zástupných commandů, ale ten princip, ať si to nekomplikujem je ten, že jsem funkci předal celou scénu, a vrátil novou.
Na tom je krásně vidět, jak tě funkcionální omezení dotlačilo do prakticky asi nejhoršího řešení. Neber to jako kritiku, ta úloha ve funkcionálním přístupu nemá žádné dobré řešení :). Co je na tom zásadně špatně:
1) Funkce by měla pracovat jen s daty, která opravdu potřebuje. Ty předáváš celou scénu, i když s ní funkce provede jen nějakou omezenou operaci. Vůbec nevíš, co se kde zpracovává za data.
2) Nejde to paralelizovat. Jednotlivé funkce musíš spouštět sekvenčně.
3) Vytvářet kvůli každé jedné izolované změně novou scénu je neefektivní.
Kdybys to napsal rovnou imperativně, bylo by to přehlednější, rychlejší a udržovatelnější.
-
A musíš hlídat, že pomeranče nemají někde schovaný odkaz na citrony.
V tom případě sdílím i citrony. A FP mi nijak nepomůže, sychronizaci citronů musím i v FP řešit stejně jako sychronizaci pomerančů, pokud jde s citronem přes pomeranč provádět nějaká akce.
A není to naopak? Že v této jediné věci ti FP moc nepomůže, zatímco ve všem ostatním ano? Zkus si přečíst naše příspěvky. Vždyť ty vůbec nereflektuješ co popisujem.
-
A není to naopak? Že v této jediné věci ti FP moc nepomůže, zatímco ve všem ostatním ano? Zkus si přečíst naše příspěvky. Vždyť ty vůbec nereflektuješ co popisujem.
V čem mi pomůže? Že nejde rozumně udělat změnu ve scéně? Zkuste si přečíst, co píšu já.
-
A není to naopak? Že v této jediné věci ti FP moc nepomůže, zatímco ve všem ostatním ano? Zkus si přečíst naše příspěvky. Vždyť ty vůbec nereflektuješ co popisujem.
V čem mi pomůže? Že nejde rozumně udělat změnu ve scéně? Zkuste si přečíst, co píšu já.
ta scéna, co je to za datovou strukturu?
-
No, ve skutečnosti jsem to dělal pomocí zástupných commandů, ale ten princip, ať si to nekomplikujem je ten, že jsem funkci předal celou scénu, a vrátil novou.
Na tom je krásně vidět, jak tě funkcionální omezení dotlačilo do prakticky asi nejhoršího řešení. Neber to jako kritiku, ta úloha ve funkcionálním přístupu nemá žádné dobré řešení :).
Vidiš to. Já bych naopak řekl, že je na tom krásně vidět, jak těžké je opustit mutable svět a začít přemýšlet funkcionálně.
Co je na tom zásadně špatně:
1) Funkce by měla pracovat jen s daty, která opravdu potřebuje. Ty předáváš celou scénu, i když s ní funkce provede jen nějakou omezenou operaci. Vůbec nevíš, co se kde zpracovává za data.
2) Nejde to paralelizovat. Jednotlivé funkce musíš spouštět sekvenčně.
Zmínil jsem ty zástupné commandy. Protože to vypadá, že si si to nedomyslel, tak popíšu:
V rámci tiku, každý objekt dostal stejnou scénu. Jeho úkolem bylo určit a vrátit co chce na té scéně změnit. Toto se dá velice dobře paralelizovat. Výsledkem byl seznam commandů, který se mají uplatnit na scéně. Toto je kritická sekce, která se paralelizuje hůř, ale taky to de. Například můžu rozdělit scénu na části, které se nemohou ovlivňovat. Třeba když je to rozděleno zdí. Pak můžu tyto dvě scény paralelizovat zcela bez problému. A dá se na tom najít další a další způsoby, jak to optimalizovat.
Proč to dělat takto a ne jinak? Protože vždycky vím, co se mění. Díky tomu, že vím kde je kritická sekce, můžu vyhmátnout, zda nejde změnit, nebo nějak optimalizovat. Což většinou jde. A to zcela bez rizika, že se mi budou množit pomeranče. Nemůže dojít k deadlocku. Nemůže se mi nic rozjet.
3) Vytvářet kvůli každé jedné izolované změně novou scénu je neefektivní.
Nevím jak v jiném jazyce, ale v Haskellu je to zadarmo. (Samozřejmě nebavíme se o vytváření nové scény od nuly, ale to už tu Prímek vysvětloval.)
Kdybys to napsal rovnou imperativně, bylo by to přehlednější, rychlejší a udržovatelnější.
Opravdu nevidím důvod si něco takového myslet. Můžeš mi to vysvětlit? Bohužel výše uvedené tři argumenty pouze naznačují, že nemáš zkušenosti jak FP funguje.
-
ta scéna, co je to za datovou strukturu?
Něco takového: https://en.wikipedia.org/wiki/Scene_graph
-
Vidiš to. Já bych naopak řekl, že je na tom krásně vidět, jak těžké je opustit mutable svět a začít přemýšlet funkcionálně.
Na tom, co jsi udělal, nic funkcionálního není. Spojuje to nevýhody imperativního a funkcionálního přistupu dohromady. Pracovat ve všech funkcích s celou scénou je totální fail. Formálně je to funkcionální, prakticky je to ale hybrid, imperativní přístup jsi násilně narouboval do funkcionálního světa. Výsledek není dobrý.
-
A není to naopak? Že v této jediné věci ti FP moc nepomůže, zatímco ve všem ostatním ano? Zkus si přečíst naše příspěvky. Vždyť ty vůbec nereflektuješ co popisujem.
V čem mi pomůže? Že nejde rozumně udělat změnu ve scéně? Zkuste si přečíst, co píšu já.
Povedzte Kefalín, čo vy si predstavujete pod takým slovom "rozumě"?
je toto dostatenčně rozumné?
scene' = setScene scene (100,100,100) pomeranc
-
Zmínil jsem ty zástupné commandy. Protože to vypadá, že si si to nedomyslel, tak popíšu:
V rámci tiku, každý objekt dostal stejnou scénu. Jeho úkolem bylo určit a vrátit co chce na té scéně změnit.
To už jsme řešili, stejný přistup jako v Pac-Manovi. Generuješ eventy, které říkají, jak se má scéna změnit a uděláš to ex-post. To taky není dobré, budeš tam mít kolize a vzájemně si odporující eventy.
-
Na tom, co jsi udělal, nic funkcionálního není. Spojuje to nevýhody imperativního a funkcionálního přistupu dohromady. Pracovat ve všech funkcích s celou scénou je totální fail. Formálně je to funkcionální, prakticky je to ale hybrid, imperativní přístup jsi násilně narouboval do funkcionálního světa. Výsledek není dobrý.
Sorry, ale tohle je totální krávovina, co jsi teď napsal. Ve FP se běžně předává celý stav, protože prostě není žádný jiný způsob, jak s ním pracovat, protože nejsou globální proměnné.
Když o FP zjevně nic nevíš, proč o něm něco tvrdíš? A ještě tak vehementně?!
Škoda, že nepíšeš pod svým jménem, protože to by sis asi dal víc záležet na tom, abys ze sebe nedělal totálního vola... :(
(sorry, ale jinak už se to fakt napsat nedalo)
-
Vidiš to. Já bych naopak řekl, že je na tom krásně vidět, jak těžké je opustit mutable svět a začít přemýšlet funkcionálně.
Na tom, co jsi udělal, nic funkcionálního není. Spojuje to nevýhody imperativního a funkcionálního přistupu dohromady. Pracovat ve všech funkcích s celou scénou je totální fail. Formálně je to funkcionální, prakticky je to ale hybrid, imperativní přístup jsi násilně narouboval do funkcionálního světa. Výsledek není dobrý.
Respektuji tvůj názor, ale nemám důvod s ním souhlasit. Jsem bohužel dost tvrdohlavý, a bez argumentů se mnou nehneš ;-)
-
Zmínil jsem ty zástupné commandy. Protože to vypadá, že si si to nedomyslel, tak popíšu:
V rámci tiku, každý objekt dostal stejnou scénu. Jeho úkolem bylo určit a vrátit co chce na té scéně změnit.
To už jsme řešili, stejný přistup jako v Pac-Manovi. Generuješ eventy, které říkají, jak se má scéna změnit a uděláš to ex-post. To taky není dobré, budeš tam mít kolize a vzájemně si odporující eventy.
Jakoby ty kolize byly jen díky tomu, že to je napsáno immutable. Ve skutečnosti je to takhle velice šikovné, protože se mi ty kolize setřesou na jedno místo, žádná mi neunikne, a mohu volit strategii. Ukaž, jak by jsi to dělal mutable, a snaž se, aby to bylo fakt lepší ;-)
-
Sorry, ale tohle je totální krávovina, co jsi teď napsal. Ve FP se běžně předává celý stav, protože prostě není žádný jiný způsob, jak s ním pracovat, protože nejsou globální proměnné.
Nikdo nemluví o globálních proměnných, proč je sem pleteš? Ve funkci, která mění pozici pomeranče, opravdu nepotřebuju celou scénu, stačí mi ten pomeranč. Počkat ono to vlasně v FP nejde... Ale jinak je FP naprosto vynikající, přináší samé výhody a jen blbec gamer ho do her nechce. Mirek Prýmek sice žádnou hru FP ani neFP neděl, ale přesně ví, co je pro gamera dobré.
-
Jakoby ty kolize byly jen díky tomu, že to je napsáno immutable. Ve skutečnosti je to takhle velice šikovné, protože se mi ty kolize setřesou na jedno místo, žádná mi neunikne, a mohu volit strategii. Ukaž, jak by jsi to dělal mutable, a snaž se, aby to bylo fakt lepší ;-)
Ne není to šikovné. V imperativním přistupu kolize vůbec nejsou, nevzniknou. Provedeš výpočet a hned ho aplikuješ na scénu. Můžeš si nalhávat, že je lepší generovat eventy se změnami a nechat to na později ale není. Zásadně zvyšuješ komplexitu řešení.
-
Ve funkci, která mění pozici pomeranče, opravdu nepotřebuju celou scénu, stačí mi ten pomeranč.
Ukaž mi, jak změníš pozici pomeranče, když budeš mět jen ten pomeranč. Klidně v Javě. Ale musíš mět jen pomeranč, to je podmínka!
-
Ukaž mi, jak změníš pozici pomeranče, když budeš mět jen ten pomeranč. Klidně v Javě. Ale musíš mět jen pomeranč, to je podmínka!
pomeranc.SetPos(1,2,3);
-
Nikdo nemluví o globálních proměnných, proč je sem pleteš?
Protože ve FP neexistuje žádný* způsob, jak uložit data, než je předat funkci. Není to žádná blbost, je to jediný* způsob, kterým se s daty pracuje.
* trochu kecám, ale to už je v kontextu toho všeho blábolení tady celkem jedno...
V imperativním přistupu kolize vůbec nejsou, nevzniknou. Provedeš výpočet a hned ho aplikuješ na scénu. Můžeš si nalhávat, že je lepší generovat eventy se změnami a nechat to na později ale není. Zásadně zvyšuješ komplexitu řešení.
Ok, tak raději mluv o tom, co znáš a ne o tom, co neznáš. Takže: máš dvě vlákna, v každém ti běží jedna postavička. Jak v ne-FP zabezpečíš, aby postavičky nevlezly na stejné políčko? Potenciálně konfliktní změny musíš tak jako tak serializovat. Čili jsi na tom úplně stejně. Nebo?
-
Jakoby ty kolize byly jen díky tomu, že to je napsáno immutable. Ve skutečnosti je to takhle velice šikovné, protože se mi ty kolize setřesou na jedno místo, žádná mi neunikne, a mohu volit strategii. Ukaž, jak by jsi to dělal mutable, a snaž se, aby to bylo fakt lepší ;-)
Ne není to šikovné. V imperativním přistupu kolize vůbec nejsou, nevzniknou. Provedeš výpočet a hned ho aplikuješ na scénu.
Takže dva panácové přijdou k pomeranči. Kdo bude mít pomeranč? Nebo to není kolize?
Můžeš si nalhávat, že je lepší generovat eventy se změnami a nechat to na později ale není. Zásadně zvyšuješ komplexitu řešení.
Kluci z facebooku si myslí něco jiného (http://facebook.github.io/react/).
-
Ukaž mi, jak změníš pozici pomeranče, když budeš mět jen ten pomeranč. Klidně v Javě. Ale musíš mět jen pomeranč, to je podmínka!
pomeranc.SetPos(1,2,3);
To jsi nastavil objektu pomeranc atribut pos na 1,2,3. To není změna pozice.
Nesplnil jsi zadání.
Zkus znova.
-
Ok, tak raději mluv o tom, co znáš a ne o tom, co neznáš.
Nápodobně.
Takže: máš dvě vlákna, v každém ti běží jedna postavička. Jak v ne-FP zabezpečíš, aby postavičky nevlezly na stejné políčko? Potenciálně konfliktní změny musíš tak jako tak serializovat. Čili jsi na tom úplně stejně. Nebo?
Nepleť dohromady serializaci a sychronizaci, to je něco úplně jiného. Vy chcete vytvářet seznam eventů o tom, co všechno se změnilo, který pak nějakou magií zpracujete, vyřešíte vzájemně si odporující eventy a rozhodnete o konečném stavu. Já to tak dělat nechci, provádím změny rovnou ve scéně, žádný seznam nevytvářím. Kdo dřív přijde, ten dřív mele. Musím řešit sychronizaci, když mám panáky ve vláknech, tak musím políčko nejdřív zamknout, než na něj panák vstoupí.
Obojí má svoje nevýhody. Řešení s eventy má jako nevýhodu nedeterminismus, rozhodnout které eventy jsou ty pravé není vůbec triviální úloha. Já tohle vůbec dělat nemusím. Řešení se sychronizací může být pomalejší, ale je to všechno o návrhu, co poběží paralelně a jaké sdílená data to může mít.
-
Kluci z facebooku si myslí něco jiného (http://facebook.github.io/react/).
A JavaScript library for building user interfaces. To asi nebude na hry, že?
-
Nápodobně.
Já mluvím o tom, co znám. Znám (jakžtakž) FP a znám i klasiku. O hrách, které neznám, netvrdím nic.
Nepleť dohromady serializaci a sychronizaci, to je něco úplně jiného.
Pokud máš dvě potenciální kolizní úpravy datové struktury, tak je musíš serializovat. Jak přesně to uděláš, je detail, ale vždycky to bude nějaká serializace.
Vy chcete vytvářet seznam eventů o tom, co všechno se změnilo, který pak nějakou magií zpracujete, vyřešíte vzájemně si odporující eventy a rozhodnete o konečném stavu.
To je jenom jeden z možných přístupů. Netvrdím, že bych to tak dělal, protože jsem žádnou netriviální hru nikdy neprogramoval.
Já to tak dělat nechci, provádím změny rovnou ve scéně, žádný seznam nevytvářím. Kdo dřív přijde, ten dřív mele.
No vidíš - a přesně takhle bys to udělal ve FP, kde by scénu držel actor. To je překvápko, že? :)
Musím řešit sychronizaci, když mám panáky ve vláknech, tak musím políčko nejdřív zamknout, než na něj panák vstoupí.
Čili operace s políčkem serializovat - úplně přesně jako při použití actoru.
Řešení se sychronizací může být pomalejší, ale je to všechno o návrhu, co poběží paralelně a jaké sdílená data to může mít.
No vidíš - a u FP žádná sdílená data řešit nemusíš. Buď máš data v ruce - a pak ti do nich nikdo lézt nemůže, neboje nemáš a pak do nich nemůžeš lézt ty. Nemusíš řešit, kdo drží odkaz na co, kde je potřeba synchronizovat co, kde ti vzniká jaký deadlock. To je celé, nic víc v tom nemusíš hledat.
-
Vy chcete vytvářet seznam eventů o tom, co všechno se změnilo
To chci dělat já. Jestli by to tak dělal Prýmek, ...
Já to tak dělat nechci, provádím změny rovnou ve scéně, žádný seznam nevytvářím. Kdo dřív přijde, ten dřív mele. Musím řešit sychronizaci, když mám panáky ve vláknech, tak musím políčko nejdřív zamknout, než na něj panák vstoupí.
Ale vždyť tě nikdo nenutí. Já bych to tak sice nedělal, páč mi to přijde hrozně pracný a bordeloizní, ale každému co jeho jest.
Řešení s eventy má jako nevýhodu nedeterminismus, rozhodnout které eventy jsou ty pravé není vůbec triviální úloha. Já tohle vůbec dělat nemusím.
Ale musíš, musíš. A mnohem víc netriviálně.
Obojí má svoje nevýhody.
Immutable řešení má jedinou nevýhodu. Změnu myšlení.
-
Řešení s eventy má jako nevýhodu nedeterminismus, rozhodnout které eventy jsou ty pravé není vůbec triviální úloha. Já tohle vůbec dělat nemusím.
Ale musíš, musíš. A mnohem víc netriviálně.
Mě fakt baví, jak gamer vykládá, jakej je strašnej problém synchronizace eventů, ale když se ho zeptáš, jak by to dělal on, tak odpoví "kdo dřív přijde...". Synchronizovat eventy stylem "kdo dřív přijde" je fakt strašnej problém :)))
-
Kluci z facebooku si myslí něco jiného (http://facebook.github.io/react/).
A JavaScript library for building user interfaces. To asi nebude na hry, že?
Kontext!
Ale věřím, že jsi se s tím nikdy nesetkal, takže pointa:
Prohlížeč si vytváří objektovou strukturu dokumentu, takzvaně DOM. Na každém uzlu můžeš pověsit posluchače událostí, a ty odpalovat, a dynamicky měnit strukturu jak chceš. Tedy, jestli jsem tě dobře pochopil, tak toto je tvůj, dle tebe výhodnější, přístup. React dělá mimojiné to, že si vytváří kopii DOM, a všechny změny, které se vytvářejí se nejdříve provádí na té kopii - takže je to trochu podobné tomu mému přístupu. Po té, co si všechny objekty uplatní všechny změny, které jsou potřeba, a React samozřejmě musí zohledňovat i konflikty atd, tak se ta změny teprve promítne do originálního DOM. Vtip je v tom, že ač se ti může zdát, že to přeci musí být strašně pomalé, tak zkušenost ukázala, že to lítá jak z praku (ačkoliv zvorat se to dá taky).
-
To jsi nastavil objektu pomeranc atribut pos na 1,2,3. To není změna pozice.
Nesplnil jsi zadání.
Zkus znova.
A jak si myslíš, že se to normálně dělá? Objekt drží informaci o své poloze a jen jemu se to nastavuje:
https://docs.unrealengine.com/latest/INT/API/Runtime/Engine/GameFramework/AActor/SetActorLocation/index.html
Nebo v Unreal Engine taky nesplnili zadání a mají to předělat podle tvých představ?
-
Řešení s eventy má jako nevýhodu nedeterminismus, rozhodnout které eventy jsou ty pravé není vůbec triviální úloha. Já tohle vůbec dělat nemusím.
Ale musíš, musíš. A mnohem víc netriviálně.
Mě fakt baví, jak gamer vykládá, jakej je strašnej problém synchronizace eventů, ale když se ho zeptáš, jak by to dělal on, tak odpoví "kdo dřív přijde...". Synchronizovat eventy stylem "kdo dřív přijde" je fakt strašnej problém :)))
Pokud tam dva paňácové šahali po pomeranči, tak ten první ho dostal, a ten druhej šáhnul do prázdna (v příším tiku by se to dozvěděl). Jednoduše, kdo dřív přijde. Podobně to fungovalo když by se měl ten paňáca hejbat, a vznikla by nějaká kolize (narazil do jiného paňáci).
Ve skutečnosti je to takhle velice šikovné, protože se mi ty kolize setřesou na jedno místo, žádná mi neunikne, a mohu volit strategii.
Hmm...
-
To jsi nastavil objektu pomeranc atribut pos na 1,2,3. To není změna pozice.
Nesplnil jsi zadání.
Zkus znova.
A jak si myslíš, že se to normálně dělá? Objekt drží informaci o své poloze a jen jemu se to nastavuje:
https://docs.unrealengine.com/latest/INT/API/Runtime/Engine/GameFramework/AActor/SetActorLocation/index.html
Nebo v Unreal Engine taky nesplnili zadání a mají to předělat podle tvých představ?
V Unreal Engine taky netvrdí, že:
Ve funkci, která mění pozici pomeranče, opravdu nepotřebuju celou scénu, stačí mi ten pomeranč.
-
A jak si myslíš, že se to normálně dělá?
No, já si sem docela pevně jist, že normálně se to dělá tak, že se vezme scéna, a ten pomeranč se na té scéně umístí kam je třeba. A obávám se, že v tomto vesmíru to prostě jinak nejde. Jo, samozřejmě jsou kolem toho různé omáčky, to je jasné. S Unrealengine sice nemám zkušenosti, ale typoval bych, že tam taky bude nějaká kritická sekce, kam se ten Actor, nebo jak to mají pojmenované, doputuje, a to se pak promítne do scény.
-
Mě fakt baví, jak gamer vykládá, jakej je strašnej problém synchronizace eventů, ale když se ho zeptáš, jak by to dělal on, tak odpoví "kdo dřív přijde...". Synchronizovat eventy stylem "kdo dřív přijde" je fakt strašnej problém :)))
Mě fakt baví, jak nechápete podstatu problému. Funkcionálním přístupem s generováním eventů jste tam přidali kompexitu navíc. Netriviální kompexitu navíc, kterou musíte řešit. A podle mě rozumně vyřešit nejde, ty eventy budou kolizní a nemáte deterministický algoritmus na řešení kolizí. Single thread imperativním přístupem žádné eventy nevznikají, nemusím je vůbec řešit. Nezajímají mě. Multi thread imperativním přistupem taky žádné eventy nevzníkají. Nezajímají mě. Musím řešit navíc synchronizaci přístupu ke sdíleným objektům. Mám to ale pořád docela dobře pod kontrolou, protože o tom, co a kdy se bude paralelizovat, rozhoduju já. Za mě je lepší můj způsob. Ale klidně si to dělejte jinak, když se vám to zdá dobré.
-
No, já si sem docela pevně jist, že normálně se to dělá tak, že se vezme scéna, a ten pomeranč se na té scéně umístí kam je třeba. A obávám se, že v tomto vesmíru to prostě jinak nejde. Jo, samozřejmě jsou kolem toho různé omáčky, to je jasné. S Unrealengine sice nemám zkušenosti, ale typoval bych, že tam taky bude nějaká kritická sekce, kam se ten Actor, nebo jak to mají pojmenované, doputuje, a to se pak promítne do scény.
Tohle mě fakt nebaví. Dělal jsem na engine AAA hry (nebyl to Unreal) a žádná kritická sekce tam nebyla. Nechceš ji tam, zamykání stojí výkon. Není tam potřeba. Nastavení pozice je prostě metoda objektu. Nepotřebuješ k tomu scénu. Všichni (kromě funkcionálních fanatiků ;)), to tak dělají.
-
Funkcionálním přístupem s generováním eventů jste tam přidali kompexitu navíc. Netriviální kompexitu navíc, kterou musíte řešit.
Zatímco když budeš řešit zamykání všude možně ve všech možných kontextech, protože kdokoli si může nechat odkaz na cokoli, a občas nějaký ten zámek zapomeneš přidat a ono ti to bude zničehožnic nedeterministicky padat, tak tím žádnou komplexitu nepřidáváš a nic řešit nemusíš :)
Hele, už toho fakt nechme, nikam to nevede. Vždyť tě nikdo nenutí FP používat, tak jakej máš problém? Prostě FP nepoužívej. Když o něm nebudeš rozšiřovat nesmysly, tak na tebe nikdo nebude reagovat a všichni budou spokojení, ne?
(https://s-media-cache-ak0.pinimg.com/236x/09/bb/2f/09bb2f9090f20c8c13a45ffb8424137b.jpg)
-
Mě fakt baví, jak nechápete podstatu problému. Funkcionálním přístupem s generováním eventů jste tam přidali kompexitu navíc. Netriviální kompexitu navíc, kterou musíte řešit. A podle mě rozumně vyřešit nejde, ty eventy budou kolizní a nemáte deterministický algoritmus na řešení kolizí. Single thread imperativním přístupem žádné eventy nevznikají, nemusím je vůbec řešit. Nezajímají mě. Multi thread imperativním přistupem taky žádné eventy nevzníkají. Nezajímají mě. Musím řešit navíc synchronizaci přístupu ke sdíleným objektům. Mám to ale pořád docela dobře pod kontrolou, protože o tom, co a kdy se bude paralelizovat, rozhoduju já. Za mě je lepší můj způsob. Ale klidně si to dělejte jinak, když se vám to zdá dobré.
Podstata problému je, že neumíš počítat (prosím neber to osobně). Ta komplexita je ve FP mnohem menší. Máš nad tím mnohem menší kontrolu. Synchronizaci přístupu musíme řešit oba, ale tobě to půjde hůř (a nebo jsi šikovný programátor, což mi ale nepomůže).
Já bych to tady uzavřel. Dokavad nesplatíš "dluhy", tak za mě nemá smysl pokračovat:
Ukaž mi, jak změníš pozici pomeranče, když budeš mět jen ten pomeranč. Klidně v Javě. Ale musíš mět jen pomeranč, to je podmínka!
Jakoby ty kolize byly jen díky tomu, že to je napsáno immutable. Ve skutečnosti je to takhle velice šikovné, protože se mi ty kolize setřesou na jedno místo, žádná mi neunikne, a mohu volit strategii. Ukaž, jak by jsi to dělal mutable, a snaž se, aby to bylo fakt lepší ;-)
Vidiš to. Já bych naopak řekl, že je na tom krásně vidět, jak těžké je opustit mutable svět a začít přemýšlet funkcionálně.
Na tom, co jsi udělal, nic funkcionálního není. Spojuje to nevýhody imperativního a funkcionálního přistupu dohromady. Pracovat ve všech funkcích s celou scénou je totální fail. Formálně je to funkcionální, prakticky je to ale hybrid, imperativní přístup jsi násilně narouboval do funkcionálního světa. Výsledek není dobrý.
Respektuji tvůj názor, ale nemám důvod s ním souhlasit. Jsem bohužel dost tvrdohlavý, a bez argumentů se mnou nehneš ;-)
-
Tohle mě fakt nebaví. Dělal jsem na engine AAA hry (nebyl to Unreal) a žádná kritická sekce tam nebyla. Nechceš ji tam, zamykání stojí výkon. Není tam potřeba. Nastavení pozice je prostě metoda objektu. Nepotřebuješ k tomu scénu. Všichni (kromě funkcionálních fanatiků ;)), to tak dělají.
Nevím proč jsem si právě vzpomněl na Larru Croft s rukou ve zdi...
Jen pro pořádek, já si kritickou sekcí představuju místo, kde může dojít ke konfliktům = dva paňácové berou stejný pomeranč. Zámky jsou v immutable nutné možná méně než v mutable světě. Chápeme se doufám dobře.
-
Hele, už toho fakt nechme, nikam to nevede.
Souhlas
Vždyť tě nikdo nenutí FP používat, tak jakej máš problém? Prostě FP nepoužívej. Když o něm nebudeš rozšiřovat nesmysly, tak na tebe nikdo nebude reagovat a všichni budou spokojení, ne?
Když přestanete tvrdit, že funkcionální programování je úplně to nejlepší na vývoj her, i když jste žádnou hru nedělali (teda BoneFlute dělal), tak přestanu reagovat. Já si to totiž nemyslím. A se mnou si to nemyslí naprostá většina ostatních vývojářů her. Kdyby to bylo tak skvělé, jak prezentujete, každý včetně mě by po tom hned skočil. Všichni chtějí dělat co nejlepší hry, konkurence je v tomto oboru velmi tvrdá.
-
Když přestanete tvrdit, že funkcionální programování je úplně to nejlepší na vývoj her, i když jste žádnou hru nedělali (teda BoneFlute dělal), tak přestanu reagovat.
To ale přece nikdo netvrdí. Já tady jenom celou dobu vyvracím tvoje předsudky vůči FP, které jsou zjevně založené na neznalosti. Je možný, že FP není pro hry vůbec vhodný a jsou pro to objektivní důvody, ale určitě to nejsou ty, které popisuješ, protože tak, jak to píšeš, to prostě není.
Něco tvrdit a vyvracet tvrzení je úplně jiná důkazní situace.
Kdyby to bylo tak skvělé, jak prezentujete, každý včetně mě by po tom hned skočil. Všichni chtějí dělat co nejlepší hry, konkurence je v tomto oboru velmi tvrdá.
Tak třeba ukázkově tohle tvrzení můžu velice lehce vyvrátit - a zároveň si nemusím myslet, že FP je na hry nejlepší, dokonce si klidně zároveň můžu myslet, že je na hry úplně špatné a nevhodné.
Tohle tvrzení je nepravdivé, protože můžou být úplně jiné důvody, proč se FP nepoužívá - například to, že C++ programátorů máš plnou ... zatímco padesát dobrých Haskellistů bych ti teda hledat nepřál. Vždyť to říká i ten Carmack: je to hezký přístup, ale nedává ekonomický smysl na něj přecházet, když máte lidi, máte nástroje, máte ekosystém. Akorát on si narozdíl od tebe nevymýšlí pseudodůvody, proč to nejde technicky.
-
Když přestanete tvrdit, že funkcionální programování je úplně to nejlepší na vývoj her, i když jste žádnou hru nedělali (teda BoneFlute dělal), tak přestanu reagovat. Já si to totiž nemyslím.
Jak si to můžeš myslet, když o FP víš tak málo?
-
Jak si to můžeš myslet, když o FP víš tak málo?
Já to nechci začínat znova... Dva lidi nezávisle (BoneFlue a implementátora Pac-Mana), dovedlo funkcionální programování k tomu, že vyrobí seznam eventů pro celou scénu a pak se ho snaží ex-post nějak aplikovat a ještě to považují za výhodu a úžasný nápad. Pro mě je to úplně uhozená představa. Nikdo to tak nedělá, i když by mohl i v imperativním přístupu prakticky zadarmo bez synchronizace.
Můžeme si samozřejmě říct, že všichni vývojáři her jsou hloupí, nepochopili funkcionální programování, ví o něm strašně málo a teprve čekají na to, až jim někdo ukáže tu správnou cestu. Occamova břitva ale říká, že to nebude ta správná teorie.
-
Jak si to můžeš myslet, když o FP víš tak málo?
Já to nechci začínat znova... Dva lidi nezávisle (BoneFlue a implementátora Pac-Mana), dovedlo funkcionální programování k tomu, že vyrobí seznam eventů pro celou scénu a pak se ho snaží ex-post nějak aplikovat a ještě to považují za výhodu a úžasný nápad. Pro mě je to úplně uhozená představa. Nikdo to tak nedělá, i když by mohl i v imperativním přístupu prakticky zadarmo bez synchronizace.
Pokud víš o FP tolik, kolik jsi dneska ukázal, tak to, že se ti něco na FP zdá úplně uhozený není vůbec směrodatné.
Dobrou noc.
-
Můžeme si samozřejmě říct, že všichni vývojáři her jsou hloupí, nepochopili funkcionální programování, ví o něm strašně málo a teprve čekají na to, až jim někdo ukáže tu správnou cestu. Occamova břitva ale říká, že to nebude ta správná teorie.
Super, na tom se konečně shodneme! Occamova břitva říká, že to tak nebude, protože kdybys to udělal v C++, tak bys tím nezískal tytéž výhody jako ve skutečně funkcionálních jazycích, protože tam nemáš ty garance.
A o tom, že se "to tak nikdo nedělá", se dá s úspěchem pochybovat, opět můžeme zůstat u Carmacka:
My pragmatic summary: A large fraction of the flaws in software development are due to programmers not fully understanding all the possible states their code may execute in. In a multithreaded environment, the lack of understanding and the resulting problems are greatly amplified, almost to the point of panic if you are paying attention. Programming in a functional style makes the state presented to your code explicit, which makes it much easier to reason about, and, in a completely pure system, makes thread race conditions impossible.
http://gamasutra.com/view/news/169296/Indepth_Functional_programming_in_C.php
Jasně, můžeš ignorovat nás, kteří jsme na "AAA enginu" nikdy nedělali, ale fakt bych rád viděl, jak by ses stejně prsil na něj a co by na to řekl on :)
-
Pokud víš o FP tolik, kolik jsi dneska ukázal, tak to, že se ti něco na FP zdá úplně uhozený není vůbec směrodatné.
Budu ignorovat osobní urážky a nebudu je vracet, i když bych mohl. Mně se to nezdá uhozený, je to logický důsledek aplikace funkcionálního přístupu na hru. Nemůžu změnit scénu, tak to udělám jinak. Vygeneruju eventy a změním ji ex-post. A teď se zkus zamyslet, brání mi něco udělat to stejné imperativně? Nebrání. Dělá to tak prakticky někdo imperativně? Nedělá. Tisíce vývojářů her každý den přemýšlí o tom, jak svoji hru udělat lepší, kvalitnější, rychlejší a jak celý vývoj dotáhnout co nejrychleji a nejúspěšněji do konce. Tohle řešení s eventy ale nepoužil nikdo.
-
Pokud víš o FP tolik, kolik jsi dneska ukázal, tak to, že se ti něco na FP zdá úplně uhozený není vůbec směrodatné.
Budu ignorovat osobní urážky a nebudu je vracet, i když bych mohl.
Urážky? Jaký urážky?
-
A o tom, že se "to tak nikdo nedělá", se dá s úspěchem pochybovat, opět můžeme zůstat u Carmacka:
Carmack je hračička a baví ho prošlapávat neprobádané cesty. S reálnou použitelností to nemusí vůbec korelovat. Jak to vůbec dopadlo s tím jeho portem Wolfensteina do Haskellu? Dotáhnul to? Nikde jsem nenašel, že ano, ale možná jsem jen špatně hledal.
-
Budu ignorovat osobní urážky a nebudu je vracet, i když bych mohl.
Je to dost smutný, neumět si přiznat, že existují věci, kterým nerozumím.
Tohle řešení s eventy ale nepoužil nikdo.
Snadno vyvratitelné. http://gamedev.stackexchange.com/questions/7718/event-driven-communication-in-a-game-engine-yes-or-no
Carmack je hračička a baví ho prošlapávat neprobádané cesty. S reálnou použitelností to nemusí vůbec korelovat.
Jistě existuje možnost, že Carmack hrám nerozumí, narozdíl od tebe, tak to špatně odhadl.
-
Jak to vůbec dopadlo s tím jeho portem Wolfensteina do Haskellu? Dotáhnul to? Nikde jsem nenašel, že ano, ale možná jsem jen špatně hledal.
To je celkem jedno, protože existuje jiná, deleko pokročilejší hra: https://wiki.haskell.org/Frag
-
Je to dost smutný, neumět si přiznat, že existují věci, kterým nerozumím.
Je to dost smutný, neumět si přiznat, že funkcionální programování není vhodné na všechno a řešení, které z funkcionálního přístupu někdy vypadnou, nejsou dobrá.
Snadno vyvratitelné. http://gamedev.stackexchange.com/questions/7718/event-driven-communication-in-a-game-engine-yes-or-no
To je něco úplně jiného, event systém na úrovni callbacků, pošlu event a nějaký callback ho hned zpracuje. Není to žádná fronta eventů na scénu, které se zpracují najednou ex-post.
Jistě existuje možnost, že Carmack hrám nerozumí, narozdíl od tebe, tak to špatně odhadl.
Můžeš ze mě dělat debila, ale aplikuješ to současně i na většinu ostatních vývojářů her. Carmackův názor je v tomto minoritní. Samozřejmě je možné, že všichni kromě Carmacka a Prýmka opravdu debilní jsou, to nepopírám.
-
Je to dost smutný, neumět si přiznat, že funkcionální programování není vhodné na všechno a řešení, které z funkcionálního přístupu někdy vypadnou, nejsou dobrá.
S tím já nemám problém. Nikdy nikde jsem netrvdil, že to tak je. Jenom tvrdím, že ty důvody jsou jiné, než uvádíš ty. Protože ty důvody, které uvádíš ty, zjevně pramení z neznalosti.
To je něco úplně jiného, event systém na úrovni callbacků, pošlu event a nějaký callback ho hned zpracuje.
Čili úplně jako v Erlangu. Co mi to přípomíná? Jo, jak jsem o něco výš napsal "úplně jako s Actorem". Sorry, ale už mě to fakt přestává bavit.
Není to žádná fronta eventů na scénu, které se zpracují najednou ex-post.
Touhle fixní ideou trpíš už dlouho, nebo tě to chytlo až v tomhle vlákně? Už tady asi tisíckrát zaznělo, že to není jediná možnost. Proč si nevyndáš ty špunty z uší?!
------
Tak. A teď už mě to přestalo bavit definitivně. Pro jistotu definitivně vypínám notifikace na tohle téma :) Dobrou noc :)
-
Touhle fixní ideou trpíš už dlouho, nebo tě to chytlo až v tomhle vlákně? Už tady asi tisíckrát zaznělo, že to není jediná možnost. Proč si nevyndáš ty špunty z uší?!
Možností je spousta, ale mně všechny připadají horší, než imperativní přístup s mutable objekty. Klidně si mysli, že jsem FP nepochopil. Já ti to vyvracet nebudu. Budeme oba spokojeni a můžeme tuto nesmyslnou debatu ukončit.
-
Možností je spousta, ale mně všechny připadají horší, než imperativní přístup s mutable objekty. Klidně si mysli, že jsem FP nepochopil. Já ti to vyvracet nebudu. Budeme oba spokojeni a můžeme tuto nesmyslnou debatu ukončit.
Ja myslim, ze v jistem smyslu je FP vzdycky "horsi", protoze v jistem smyslu omezuje tvoji svobodu. Asi jako je treba strukturovane nebo modularni programovani horsi nez spagety s globalnim stavem (videl jsem extremne efektivni, ale take extremne neprehledne, programy napsane tim druhym zpusobem). Na co se sazi je, ze urcita konstantni neefektivita (treba to, ze budes kopirovat zmeneny objekt a ne ho menit na miste) je nakonec mensi nevyhodou nez celkova neprehlednost programu. A taky se sazi na to, ze s tim prvnim se kompilatory eventualne vyporadaji (coz u FP zatim tak docela neplati, ale u normalnich kompilatoru take trvalo, nez se naucily vyrobit ze strukturovanych a modularnich programu opet ty velmi efektivni spagety - napriklad inlinovanim funkci).
Vezmeme si treba to FRP - functional reactive programming - paradigma. Tam se explicitne konstruuje graf udalosti, ktere se zpracovavaji. Na nizsi urovni jsou to treba vstupy z klavesnice, na vyssi pak jednotlive zmenene objekty az na te nejvyssi cela zmenena scena. To je prehlednejsi (aspon co tvrdi zastanci tohoto pristupu) nez mutable objekty proto, protoze explicitne vidis, odkud jaka zmena vychazi; pokud ale muzes kdekoli v programu menit stav nejakeho objektu, tahle informace je v nem pouze implicitni (musis precist cely program, abys ji dohledal). Od kompilatoru se pak ocekava, ze nebude ve skutecnosti posilat udalosti, ale ze ten cely graf nejak "pochopi" a udela z nej ten puvodni system, kde se vsechno jen mutuje ve spravnem poradi (coz je zatim hudba budoucnosti, proto to asi AAA studia nepouzivaji).
Takze IMHO, pokud nevidis prinos v te prehlednosti, kterou potencialne FP muze prinest, treba explicitni zapis toho grafu zavislosti, pak to rozhodne nema smysl pouzit jako metodologii. Nicmene vyvoj programovani se nepochybne ubira timhle smerem - od efektivnich, ale neprehlednych, zapisu k zapisum prehlednejsim (a abstraktnejsim), kde tu efektivitu nejak (snad) obstara kompilator.
(Kdyz jsme tak u toho, FP se casto "prodava" s tim, ze umoznuje vice paralelismu. To je IMHO az druhorady efekt, podobne jako strukturovane programovani muze pomoci snazsimu zapisu algoritmu a tudiz umoznuje pouzit/vyzkouset lepsi/jine algoritmy. Primarni vyhoda FP je IMHO zlepseni prehlednosti operaci s daty.)
-
(Kdyz jsme tak u toho, FP se casto "prodava" s tim, ze umoznuje vice paralelismu. To je IMHO az druhorady efekt, podobne jako strukturovane programovani muze pomoci snazsimu zapisu algoritmu a tudiz umoznuje pouzit/vyzkouset lepsi/jine algoritmy. Primarni vyhoda FP je IMHO zlepseni prehlednosti operaci s daty.)
Presne tak - prvorada vyhoda FP je, ze je tam explicitni a prehledne data flow - pokud data nekam neposles, tak je tam proste nemas k dispozici. Z toho pak plynou ruzne veci, jako napr. ze muzes rychle a presne rozhodnout, v jake casti kodu se jaka data meni a jaka zarucene nemeni, jaky kod je zavisly na jakych datech atd. Moznost paralelizace je pak dusledek tohodle.
-
Ja myslim, ze v jistem smyslu je FP vzdycky "horsi", protoze v jistem smyslu omezuje tvoji svobodu.
Nejde o to, že FP omezuje svobodu, to je v pohodě, pokud omezení něco přinese. Problém je ale v tomto konkrétním případě mnohem hlubší, FP vede k horšímu technickému řešení.
Řeším složitý dynamický svět s velkým množstvím objektů, které vzájemně interagují. Z definice je to jeden svět, který se mění.
Klasický imperativní přístup respektuje tuto definici, když chci spočítat nový stav, tak postupně iteruji přes všechny objekty, spočítám jim nový stav a taky jim ho hned nastavím. Mezi objekty jsou vzájemné závislosti, když spočítám, že panák 1 vleze na políčko A, tak ho tam rovnou umístím. Panák 2, kterého počítám vzápětí, zná aktuální polohu panáka 1 a ví, že na políčko A nemůže, vybere si třeba B. Takový přímočarý přístup má jednu obrovskou výhodu, na konci výpočtu mám vždy celý svět v konzistentním stavu, nikdy se nemůže stát, že jsou dva panáci na políčku A.
Teď na to chci napasovat funkcionální přístup, chci mít konstatní svět a výpočet bez side effectů. Ono to jde, ale musím si uvědomit, co mi to přinese a co mi to vezme. V zásadě to můžu udělat dvěma způsoby.
První je analogií imperativního přístupu, pokaždé když nastavím nový stav objektu, tak vyrobím celý nový "správný" svět. To bude fungovat naprosto stejně jako imperativní přístup. Má to ale svoje ale. I kdyby výroba nového světa nestála žádný výkon navíc (čemuž nevěřím), tak je takový design silně diskutabilní. Mám prakticky netestovatelné funkce, když funkce vrací celý svět, může potenciálně změnit cokoliv. Vůbec nemám přehled o datech, která do funkcí vstupují a které funkce vrací, z interface to není vidět.
Druhý způsob je nedělat změny ve světě ihned, ale schovat si je na později. Vytvořím seznam událostí, co se má se světem stát. Problém tohoto přístupu je v tom, že dává chybné výsledky a nekonzistentní svět. Když počítám všechno z původního světa, můžou se rozhodnout panácí 1 i 2, že oba vstoupí na pole A. Teď co s tím? Nechám panáka 2 tam, kde je, nebo spustím ex-post znovu AI a najdu pro panáka 2 novou pozici? Obě řešení jsou špatná. Hráč očekává, že panák 2 něco udělá a ne že bude občas "zasekávat". Spouštět znovu AI je nekoncepční a stojí to výkon. I když jde o naprosto elemetární příklad, tak stejně nevím, co s tou kolizí udělat. V reálně hře budou tisíce různých událostí a miliony jejich možných vzájemných kombinací. Já nemám dost dobrý mozek na to, abych dokázal domyslet, jak řešit všechny možné kolize. Nevybral bych si toto řešení ani náhodou. Z mého pohledu je nedeterministické.
Pak jsou samozřejmně možné kombinace obou řešení, spočítám události jen pro některé objekty a až potom vytvořím nový svět, nebo můžu svět držet někde bokem a události do něj posílat asynchronně... fantazii se meze nekladou. Ale bohužel to vždycky vede buď na vytváření nového světa nebo k sekvečnímu zpracování událostí.
Nechtěl bych, aby to vyznělo tak, že imperativní přístup je jediný správný a žádné problémy nemá. Samozřejmně že má. Ale pro mě přináší oproti FP dvě zásadní věci, umožňuje mi udržet rozhraní minimalistické (nemusím všude pracovat s celým světem) a umožňuje mi udržet svět v konzistentním stavu.
-
Tato diskuze je velmi zajimava :).
... Ale pro mě přináší oproti FP dvě zásadní věci, umožňuje mi udržet rozhraní minimalistické (nemusím všude pracovat s celým světem) a umožňuje mi udržet svět v konzistentním stavu.
V programovani her mam zkusenosti jen s tvorbou modu pro Minecraft, je mozne, ze to jde delat i jinak (nebylo by to poprve). Ale to s minimalistickym rozhranim (minimalne v MC) proste neplati. Napr. z kazde entity mohu vytahnout instnaci sveta a delat uplne cokoliv. To je jen iluze minimalniho rozhrani, pokud stejne mohu pristupovat kamkoliv - je to stejne jako f(world) -> world ve FP. Akorat tam je to explicitne receno, ze se s tim svetem neco muze stat, zatimco pri player.setDirection(1,0,0) nepoznam, jestli to nahodou nezasahuje i do jinych casti sveta. Nebo se v "opravdovych" hrach toto takto neresi (zadne zpetne reference atp.)?
-
V programovani her mam zkusenosti jen s tvorbou modu pro Minecraft, je mozne, ze to jde delat i jinak (nebylo by to poprve). Ale to s minimalistickym rozhranim (minimalne v MC) proste neplati. Napr. z kazde entity mohu vytahnout instnaci sveta a delat uplne cokoliv. To je jen iluze minimalniho rozhrani, pokud stejne mohu pristupovat kamkoliv - je to stejne jako f(world) -> world ve FP. Akorat tam je to explicitne receno, ze se s tim svetem neco muze stat, zatimco pri player.setDirection(1,0,0) nepoznam, jestli to nahodou nezasahuje i do jinych casti sveta. Nebo se v "opravdovych" hrach toto takto neresi (zadne zpetne reference atp.)?
Ano, většinou to bohužel jde, ale to neznamená, že by se to tak mělo dělat. Vytáhnout instanci světa z entity a pak měnit svět ve funkci, která dostává jako parametr entitu, je ovšem nutné považovat za prasárnu. Nic není ideální, i takové prasárny se bohužel dělají.
-
Takže tu opět máme nebetyčný rozdíl: zatímco v FP se svět přenáší do funkce jako svět, na jednom jasně definovaném místě, což je strašně neelegantní, v ne-FP se přenáší svět do funkce všude možně ve všech možných properties všech možných objektů. Čili to druhé je minimalistické rozhraní a to první je tragédie, kvůli které se hry v FP psát nedají.
A abysme nezapomněli, ten svět se musí při každém volání funkce kopírovat!
:)))))
-
Zajímalo by mě, na jaké nevýhody můžu u FP narazit. Zatím, co vím, tak že takovej Haskell je výkonově na úrovni Javy. Což zase není tak hrozné, a možná by to chtělo nějaké lepší benchmarky. Napadá vás něco dalšího? Možná sem jen příliš velkej fanda, ale když bych se měl rozmýšlet, zda C++ nebo Haskell, tak neváhám ani okamžik (a to jsem si s C++ pár věcí napsal).
-
Tato diskuze je velmi zajimava :).
... Ale pro mě přináší oproti FP dvě zásadní věci, umožňuje mi udržet rozhraní minimalistické (nemusím všude pracovat s celým světem) a umožňuje mi udržet svět v konzistentním stavu.
V programovani her mam zkusenosti jen s tvorbou modu pro Minecraft, je mozne, ze to jde delat i jinak (nebylo by to poprve). Ale to s minimalistickym rozhranim (minimalne v MC) proste neplati. Napr. z kazde entity mohu vytahnout instnaci sveta a delat uplne cokoliv. To je jen iluze minimalniho rozhrani, pokud stejne mohu pristupovat kamkoliv - je to stejne jako f(world) -> world ve FP. Akorat tam je to explicitne receno, ze se s tim svetem neco muze stat, zatimco pri player.setDirection(1,0,0) nepoznam, jestli to nahodou nezasahuje i do jinych casti sveta. Nebo se v "opravdovych" hrach toto takto neresi (zadne zpetne reference atp.)?
Já jsem tomu objektu nepředával celý svět, ale jen adekvátní výřez (zjednodušení, ve skutečnosti jsem to měl optimalizované ještě jinak). Například když byl objekt jakože v místnosti, tak dostal jen tu místnost. Protože za zeď nevidí, že jo. Mělo to ten efekt, že jsem to mohl paralelizovat, každá "místnost" mohla běžet samostatně. Myslím, že Prýmek tomu říkal partioning.
-
Já jsem tomu objektu nepředával celý svět, ale jen adekvátní výřez (zjednodušení, ve skutečnosti jsem to měl optimalizované ještě jinak).
Jo, to je právě na FP hezký - protože máš stav explicitní, na jednom místě, víš dobře, co souvisí s čím, co k výpočtu potřebuješ a co nepotřebuješ. Takže můžeš krásně udělat řez na pravém místě a ty dvě (popř. víc) částí zpracovávat zvlášť. U OOP máš síť, kde je obvykle všechno spojený se vším a řez neuděláš. Čili ani nic neparalelizuješ. Leda triviálně - zkusíš to pustit ve víc vlákneš a zamykáš, zamykáš, zamykáš, čili jestli se to zrychlí nebo nezrychlí nemáš nikdy šanci odhadnout, protože nevíš, kolikrát se ty zámky potkají...
Vlastně docela podobným způsobem zjistíš i které části dat už nikdy nebudeš potřebovat a s klidem je zahodíš. Má to hodně zajímavé efekty: https://plus.google.com/+MiroslavPrymek/posts/VyNUxy5pqei
-
No, o výhodách mi vyprávět nemusíš ;-) Mě by spíše zajímaly nějaké problémy, nevýhody, co se v FP řeší výrazně hůř, než v mutable světě.
-
Já jsem tomu objektu nepředával celý svět, ale jen adekvátní výřez (zjednodušení, ve skutečnosti jsem to měl optimalizované ještě jinak). Například když byl objekt jakože v místnosti, tak dostal jen tu místnost. Protože za zeď nevidí, že jo. Mělo to ten efekt, že jsem to mohl paralelizovat, každá "místnost" mohla běžet samostatně. Myslím, že Prýmek tomu říkal partioning.
To zni zajimave, bohuzel si nejsem jisty, jestli to muze vsude fungovat - napr. ten zminovany Minecraft. Kdyz mam svet rozdeleny na casti (chunky - 16x16x256 bloku IIRC), tak hrac na kraji jednoho chunku zcela jiste muze (a casto bude) interagovat z blokem z jineho chunku. Mozna to rozsekat podle maximalniho mozneho dosahu jakekoliv interakce s prostredim, nebo podle dohlednosti? Co jsem kod videl posledne, tak se to proste neresilo - pro kazdy svet bylo vse serializovane (ticky bloku, entit, tile entit).
Zajímalo by mě, na jaké nevýhody můžu u FP narazit. Zatím, co vím, tak že takovej Haskell je výkonově na úrovni Javy. Což zase není tak hrozné, a možná by to chtělo nějaké lepší benchmarky. ...
To me celkem zarazilo, myslel jsem, ze Oracli HotSpot ma celkem vychytany optimalizace za behu. Nebo ze by ten funkc. pristup byl o tolik lepe optimalizovatelny (ve Scale jsem pozoroval spise opak :()?
-
Zajímalo by mě, na jaké nevýhody můžu u FP narazit. Zatím, co vím, tak že takovej Haskell je výkonově na úrovni Javy. Což zase není tak hrozné, a možná by to chtělo nějaké lepší benchmarky. Napadá vás něco dalšího? Možná sem jen příliš velkej fanda, ale když bych se měl rozmýšlet, zda C++ nebo Haskell, tak neváhám ani okamžik (a to jsem si s C++ pár věcí napsal).
http://www.rarous.net/weblog/448-deset-duvodu-proc-nepouzivat-funkcionalni-jazyky.aspx :)
-
Nejde o to, že FP omezuje svobodu, to je v pohodě, pokud omezení něco přinese. Problém je ale v tomto konkrétním případě mnohem hlubší, FP vede k horšímu technickému řešení.
To jsem ti prece psal. Je to uplne analogicka situace te pred 50 lety, kdyz pouzivani mnoha malych funkci take vedlo k horsimu technickemu reseni, protoze kompilatory neumely inlining. Takze pokud mas za to, ze se ti to neoplati, nepouzivej to.
Musis si uvedomit, ze to "kopirovani" stavu sveta je jen formalismus. Dostatecne chytry kompilator to delat nemusi, pokud prijde na to, ze ten predchozi stav uz nikde nepotrebujes.
Řeším složitý dynamický svět s velkým množstvím objektů, které vzájemně interagují. Z definice je to jeden svět, který se mění.
Klasický imperativní přístup respektuje tuto definici, když chci spočítat nový stav, tak postupně iteruji přes všechny objekty, spočítám jim nový stav a taky jim ho hned nastavím. Mezi objekty jsou vzájemné závislosti, když spočítám, že panák 1 vleze na políčko A, tak ho tam rovnou umístím. Panák 2, kterého počítám vzápětí, zná aktuální polohu panáka 1 a ví, že na políčko A nemůže, vybere si třeba B. Takový přímočarý přístup má jednu obrovskou výhodu, na konci výpočtu mám vždy celý svět v konzistentním stavu, nikdy se nemůže stát, že jsou dva panáci na políčku A.
A to ti nevadi, ze vysledek vypoctu zalezi na tom, co jsi oznacil za panaka 1 a za panaka 2?
Nicmene, oba dva pristupy, ktere popisujes, a mnohe dalsi, lze realizovat funkcionalne. V podstate lze celou tu zalezitost abstrahovat do vhodne monady (tyto mohou - mimo jine - predstavovat zpusoby, jak skladat vypocty). Ten prvni zpusob odpovida klasicke stavove monade, kdy se za stav bere samotny stav sveta. Druhy zpusob - nenapsal jsi ovsem co by to melo byt; dejme tomu, ze se v tom pripade ta kolize nejak dodatecne vyresi (treba zvitezi silnejsi postava) - lze take schovat do vhodne monady. Muzeme mit napriklad i treti zpusob, ktery v pripade kolize vrati obe postavy v puvodnim stavu; tomu by zase odpovidala jina monada.
Ve funkcionalnim pristupu si muzes explicitne vybrat, jakym zpusobem ty stavy sveta (nebo i jenom podstavy) skladat (prostrednictvim jake monady). To je cele, je to jen formalismus, jestli se skutecne pak bude v tom programu neco kopirovat nebo se bude neco vyhodnocovat pozdeji je jina otazka (z hlediska efektivity nicmene podstatna), totiz otazka co s tim svede kompilator.
Nechtěl bych, aby to vyznělo tak, že imperativní přístup je jediný správný a žádné problémy nemá. Samozřejmně že má. Ale pro mě přináší oproti FP dvě zásadní věci, umožňuje mi udržet rozhraní minimalistické (nemusím všude pracovat s celým světem) a umožňuje mi udržet svět v konzistentním stavu.
Jak uz bylo receno, pokud muzes kdekoli ten svet zmenit, tak s nim fakticky vsude pracujes, akorat to explicitne nerikas. Uplny analog tomu je pouzivat vsude stavovou monadu se stavem celeho sveta pro skladani tech zmen stavu. Pak samozrejme pouzitim FP nic neziskas (je to podobne asi jako pouzivat staticky typovy system jen s jednim univerzalnim typem, ktery odpovida hodnote v dynamicky typovanem jazyce). Vyhodu ti to prinese az v momente, kdy nahlednes, ze existuji i jine zpusoby, jak skladat stavy jednotlivych prvku sveta, nez mutovat cely svet; pak je muzes vhodne funkcionalne abstrahovat.
A z toho co rikas mi tak trochu pripada, ze to vlastne delat nechces, kdyz rikas, ze by to "bylo slozite". Obavam se ale, ze tim, ze tohle nechces resit, sis ten problem zjednodusil az prilis a riskujes vic chyb (prikladem muze byt treba ta zahada, ze panak 1 ma vzdycky prednost pred panakem 2). Ono nakonec proti gustu - ja treba nemam rad staticke typovani, prestoze si uvedomuji, ze definovat si treba vysku a vahu jako ruzne typy muze vest k vetsi korektnosti programu. Je proste dobre si uvedomit, o co se tou volbou pripravujes.
-
To zni zajimave, bohuzel si nejsem jisty, jestli to muze vsude fungovat - napr. ten zminovany Minecraft. Kdyz mam svet rozdeleny na casti (chunky - 16x16x256 bloku IIRC), tak hrac na kraji jednoho chunku zcela jiste muze (a casto bude) interagovat z blokem z jineho chunku. Mozna to rozsekat podle maximalniho mozneho dosahu jakekoliv interakce s prostredim, nebo podle dohlednosti? Co jsem kod videl posledne, tak se to proste neresilo - pro kazdy svet bylo vse serializovane (ticky bloku, entit, tile entit).
Nezapomínej, že to rozdělení musí být logické, ne adminstrativní. A za druhé, rozdělovat to můžeš a nemusíš. A klidně to můžeš dělat i během života (postaví zeď atd).
-
To me celkem zarazilo, myslel jsem, ze Oracli HotSpot ma celkem vychytany optimalizace za behu. Nebo ze by ten funkc. pristup byl o tolik lepe optimalizovatelny (ve Scale jsem pozoroval spise opak :()?
Je tam pár věci, které dost zajímavě pomáhají. Třeba u GC se dá využít toho že všechny ukazatele ukazují na starší data. A high level optimalizace v Haskellu jsou něco, na co jsem ze začátku koukal jako z jara. V komentářích se dá optimalizátoru i poradit, jak má kód upravovat. Třeba z nějakého nagenerování a součtu seznamu dokáže ten seznam komplet vyhodit a převést to na cyklus.
U Scaly bych to možná svedl i na to, že na rozdíl od Haskellu nepoužívá specializovaný runtime.
-
To me celkem zarazilo, myslel jsem, ze Oracli HotSpot ma celkem vychytany optimalizace za behu. Nebo ze by ten funkc. pristup byl o tolik lepe optimalizovatelny (ve Scale jsem pozoroval spise opak :()?
Třeba u GC se dá využít toho že všechny ukazatele ukazují na starší data.
Pokud máte rekurzi a líné datové struktury, tak to nemusí platit - například v Haskellu to neplatí.
-
Včera jsem narazil na zatím nejhezčí příklad využití protokolů bez dědičnosti. Potřeboval jsem naimplementovat strukturu rysů s unifikací. Ve strukturách obvykle můžou být atomické hodnoty (zde pouze řetězec) a rekurzivně struktury rysů samy (někdy také množiny a případně další kolekce, ale to by už bylo triviální). Takto to jde nejjednodušeji:
protocol Unifiable : CustomStringConvertible {
func unifiedWith(value:Unifiable) -> Unifiable?
}
class FeatureStructure : Unifiable {
var features:[String:Unifiable] = [:]
subscript(key:String) -> Unifiable? {
get { return features[key] }
set(value) { features[key] = value }
}
func unifiedWith(value:Unifiable) -> Unifiable? {
if let value = value as? FeatureStructure {
// some complicated stuff
}
return nil
}
var description:String {
return "[" + ",".join(features.map { (key,value) in "\(key)=\(value)" }) + "]"
}
}
extension String : Unifiable {
func unifiedWith(value:Unifiable) -> Unifiable? {
if let value = value as? String { return value == self ? self : nil }
return nil
}
public var description:String { return self }
}
let fs1 = FeatureStructure()
fs1["a"] = "1"
let fs2 = FeatureStructure()
fs2["b"] = "2"
print("\(fs1) ⨆ \(fs2) = \(fs1.unifiedWith(fs2))")
Elegance sama :)
-
Swift neznám, díky tomu snippetu by se ze mě ale konvertita nestal. Musím říci, že já osobně dostávám z myšlenky "nulovatelných typů" kopřivku, ale zajímavé by bylo porovnat "opravdovou" verzi třeba právě s implementací v Haskellu. Protože třeba v Pythonu si to dovedu představit díky ducktypingu za podstatně menších bolestí (jasně, ty třídy by bez dědičnosti nebyly ocejchovány ala Unifiable?, ale ta nulovatelnost podle mě stejně degraduje typový systém, takže to podle mě vyjde skoro nastejno).
-
Nezapomínej, že to rozdělení musí být logické, ne adminstrativní.
Logicke rozdeleni to je z pohledu vykonu - pri kazde zmene chunku (napr. zniceni bloku) je treba znovu vytvorit mesh (3d "model" chunku, rendrovat blok po bloku to opravdu nejde, vytvori se jen model s viditelnymi stranami bloku), coz je draha operace a pri nevhodne velikosti to povede k vykonostnim problemum (velky chunk -> casteji a dlouho se generuje, maly chunk -> vice rezie).
A klidně to můžeš dělat i během života (postaví zeď atd).
Po pravde, az takto dynamicke rozdelovani sveta zni velmi narocne. Kdyz si vezmu, ze jen hloupy algoritmus pro svetlo (trivialni flood-fill, navic s podstatne nizsim polomerem nez je napr. dostrel luku) pusobi vykonostni problemy.
-
Musím říci, že já osobně dostávám z myšlenky "nulovatelných typů" kopřivku ... ale ta nulovatelnost podle mě stejně degraduje typový systém, takže to podle mě vyjde skoro nastejno.
Tak používej nullové singletony, které to elegantně řeší.
-
Řekněme, že chci mít objekty čtverec, obdélník, kosočtverec a rovnoběžník. Je jasné, že s jednoduchou dědičností se daleko nedostanu. Proto obecný dotaz: existuje nějaké ... ?
... Omezená dědičnost (je něco lepšího než OOP?)
Doufám, že jste tazatelovi pomohli ...
-
Musím říci, že já osobně dostávám z myšlenky "nulovatelných typů" kopřivku ... ale ta nulovatelnost podle mě stejně degraduje typový systém, takže to podle mě vyjde skoro nastejno.
Tak používej nullové singletony, které to elegantně řeší.
Když já bych raději option types a pattern matching. ;-)
-
Ty dvě počítané vlastnosti jsou tam zcela zbytečné. A co je zbytečné, musí pryč.
Ty počítané vlastnosti rozhodně nejsou zbytečné. Slouží k tomu aby šel čtverec použít všude tam, kde se dá použít rovnoběžník. Pokud mám třeba funkci na kreslení rovnoběžníka, tak pokud má čtverec tyhle dvě "zbytečné" vlastnosti tak ho tímhle způsobem můžu nakreslit taky.
Nebo chci přidat funkci pro obvod. Ve tvém případě musím tu funkci dodat do interface a doimplementovat ve všech odvozených třídách. Není jednodušší napsat jedinou funkci, která spočítá obvod rovnoběžníka a v případě čtverce využívá ty "zbytečné" počítané vlastnosti?
Jak uz od zacatku tady nekdo rikal, vsechno zavisi na zpusobu pouziti.
Tyhle zobecnovani, co tady delas zas vedou tomu, ze casto to co kompl zvladl pred 20 lety s tehdejsim hw, dela dneska na 20 let novejsim hw stejnou dobu. Kdyz budes mit miliardy ctvercu a budes je nekde muset skladovat a budes potrebovat pocitat jejich obsahy, tak si sakra rozmyslis, jestli tam budes nekde pocitat cos, drzet si zbytecne data, atp.
Odtud pak vznikaji napriklad tragicke aplikace v Jave - nepouzitelny lagovaci moloch, prestoze Java sama o sobe umi byt i relativne hodne rychla.
-
Jak uz od zacatku tady nekdo rikal, vsechno zavisi na zpusobu pouziti.
Tyhle zobecnovani, co tady delas zas vedou tomu, ze casto to co kompl zvladl pred 20 lety s tehdejsim hw, dela dneska na 20 let novejsim hw stejnou dobu. Kdyz budes mit miliardy ctvercu a budes je nekde muset skladovat a budes potrebovat pocitat jejich obsahy, tak si sakra rozmyslis, jestli tam budes nekde pocitat cos, drzet si zbytecne data, atp.
Já znám protokoly primárně z Haskellu. Nevím jak jinde ale v Haskellu tu potřebnou tabulku funkcí hlídá a předává překladač. A taky ty funkce ochotně inlinuje všude, kde to jenom jde. Takže pokud budu mít miliardy čtverců a budu si jistý tím že to jsou čtverce, tak mi překladač zainlinuje volání a kosinus vyoptimalizuje pryč.
Zrovna protokoly jsou abstrakce, která umí být překvapivě levná. Teda aspoň v příčetně navrženém jazyce.
-
Já znám protokoly primárně z Haskellu. Nevím jak jinde ale v Haskellu tu potřebnou tabulku funkcí hlídá a předává překladač. A taky ty funkce ochotně inlinuje všude, kde to jenom jde. Takže pokud budu mít miliardy čtverců a budu si jistý tím že to jsou čtverce, tak mi překladač zainlinuje volání a kosinus vyoptimalizuje pryč.
Zrovna protokoly jsou abstrakce, která umí být překvapivě levná. Teda aspoň v příčetně navrženém jazyce.
Haskell má lenošení v popisu práce - bez něj by byl nepoužitelný. Objektové jazyky na tom bývají hůř, protože když to nezvládnou zoptimalizovat při překladu, tak za běhu to už nedohoní a ten kosinus budou počítat vždy.
Překladače pro OO jazyky umí obvykle velmi dobře optimalizovat uvnitř jedné třídy. Prakticky všechny vnitřní metody inlinují, takže je úplně jedno, jestli mám jednu velkou metodu nebo 20 drobných, které se volají mezi sebou. Všechny privátní gettery a settery konvertují na přímé přístupy k atributům.
Vně třídy však už tolik možností nemají - proto public gettery/settery a public atributy nepoužívám. Jsou moc drahé. Místo toho dávám kód, který s těmi atributy potřebuje pracovat, přímo dovnitř třídy a tím optimalizaci umožním.
-
A to ti nevadi, ze vysledek vypoctu zalezi na tom, co jsi oznacil za panaka 1 a za panaka 2?
To mi vůbec nevadí, pro mě je důležité mít svět v konzistentním stavu, což je vždy zaručeno.
Jak uz bylo receno, pokud muzes kdekoli ten svet zmenit, tak s nim fakticky vsude pracujes, akorat to explicitne nerikas. Uplny analog tomu je pouzivat vsude stavovou monadu se stavem celeho sveta pro skladani tech zmen stavu. Pak samozrejme pouzitim FP nic neziskas
S tím naprosto souhlasím.
Vyhodu ti to prinese az v momente, kdy nahlednes, ze existuji i jine zpusoby, jak skladat stavy jednotlivych prvku sveta, nez mutovat cely svet; pak je muzes vhodne funkcionalne abstrahovat.
Já ty jiné způsoby vidím a vidím je i bez FP, problém je jinde. Řeším svět tak složitý, že ho nedokážu rozumně rozdělit na nezávislé části. Proto volím imperativní přístup s mutable světem a postupným výpočtem. Tím ten problém zredukuju na výpočet interakce jednoho objektu s okolním (v tu chvíli konstantním) světem. Pokud provedu nezávislé výpočty a snažím se je dát dohromady, je to problém řádově složitější, každý objekt je v interakci s každým.
Zatím všechny velké hry, které jsem viděl, to dělají právě takto, např. v Unreal Engine: https://udn.epicgames.com/Three/ActorTicking.html
Prakticky nic paralelního tam není. Některé entity se dají pustit paralelně se simulací fyziky, ale to je dost slabá optimalizace, hodí se to jen pro jednoduché entity bez fyziky. Oni by to taky rádi udělali paralelně, protože update entit v game threadu bývá dost často slabým místem z hlediska výkonu, ale prostě to neumí líp. A nemyslím si, že FP jim s tím pomůže.
A z toho co rikas mi tak trochu pripada, ze to vlastne delat nechces, kdyz rikas, ze by to "bylo slozite".
Ano nechci, z mého pohledu je to složité.
-
Swift neznám, díky tomu snippetu by se ze mě ale konvertita nestal. Musím říci, že já osobně dostávám z myšlenky "nulovatelných typů" kopřivku, ale zajímavé by bylo porovnat "opravdovou" verzi třeba právě s implementací v Haskellu. Protože třeba v Pythonu si to dovedu představit díky ducktypingu za podstatně menších bolestí (jasně, ty třídy by bez dědičnosti nebyly ocejchovány ala Unifiable?, ale ta nulovatelnost podle mě stejně degraduje typový systém, takže to podle mě vyjde skoro nastejno).
V dynamickém jazyku to takto půjde vždy (Python, ObjC), zde je výhoda právě ve statickém typování.
-
Ty dvě počítané vlastnosti jsou tam zcela zbytečné. A co je zbytečné, musí pryč.
Ty počítané vlastnosti rozhodně nejsou zbytečné. Slouží k tomu aby šel čtverec použít všude tam, kde se dá použít rovnoběžník. Pokud mám třeba funkci na kreslení rovnoběžníka, tak pokud má čtverec tyhle dvě "zbytečné" vlastnosti tak ho tímhle způsobem můžu nakreslit taky.
Nebo chci přidat funkci pro obvod. Ve tvém případě musím tu funkci dodat do interface a doimplementovat ve všech odvozených třídách. Není jednodušší napsat jedinou funkci, která spočítá obvod rovnoběžníka a v případě čtverce využívá ty "zbytečné" počítané vlastnosti?
Jak uz od zacatku tady nekdo rikal, vsechno zavisi na zpusobu pouziti.
Tyhle zobecnovani, co tady delas zas vedou tomu, ze casto to co kompl zvladl pred 20 lety s tehdejsim hw, dela dneska na 20 let novejsim hw stejnou dobu. Kdyz budes mit miliardy ctvercu a budes je nekde muset skladovat a budes potrebovat pocitat jejich obsahy, tak si sakra rozmyslis, jestli tam budes nekde pocitat cos, drzet si zbytecne data, atp.
Odtud pak vznikaji napriklad tragicke aplikace v Jave - nepouzitelny lagovaci moloch, prestoze Java sama o sobe umi byt i relativne hodne rychla.
Vtip je právě v tom, že data se nikde zbytečně nedrží, už se to tady vysvětlovalo asi třikrát. Pokud je navíc struct pro čtverec neměnná, překladač z přístupu k side2 udělá přímý přístup k side1 a skew bude prostě konstanta. Díky této fázi optimalizací lze pak vyhodit i cos z funkce pro výpočet pro obsah (z metody area bude něco jako area<T> s různou implementací pro různé typy). Takže "miliardy čtverců" v něčem jako Array<Square> by rychleji implementovat ani nešla. Stačí si ten kód pustit s profilerem, pole miliardy čtverců zabere třetinu paměti oproti miliardě rovnoběžníků, pričemž výpočet obsahu je stejně rychlý (s -Ofast pochopitelně).
-
Řekněme, že chci mít objekty čtverec, obdélník, kosočtverec a rovnoběžník. Je jasné, že s jednoduchou dědičností se daleko nedostanu. Proto obecný dotaz: existuje nějaké ... ?
... Omezená dědičnost (je něco lepšího než OOP?)
Doufám, že jste tazatelovi pomohli ...
Vzhledem k obecnosti otázky celkem jo, až na to zaplevelení diskusí o FP ve hrách.
-
Haskell má lenošení v popisu práce - bez něj by byl nepoužitelný. Objektové jazyky na tom bývají hůř, protože když to nezvládnou zoptimalizovat při překladu, tak za běhu to už nedohoní a ten kosinus budou počítat vždy.
Tady ale lazy vyhodnocení ničemu nepomůže. Já mluvil o optimalizacích při kompilaci. Lazy vyhodnocení dělá něco trochu jiného.
Trochu podobnou optimalizaci dělají i OO jazyky. C++ umí taky "odvirtualizovat" volání funkce, pokud si je jisté typem.
-
Ty dvě počítané vlastnosti jsou tam zcela zbytečné. A co je zbytečné, musí pryč.
Ty počítané vlastnosti rozhodně nejsou zbytečné. Slouží k tomu aby šel čtverec použít všude tam, kde se dá použít rovnoběžník. Pokud mám třeba funkci na kreslení rovnoběžníka, tak pokud má čtverec tyhle dvě "zbytečné" vlastnosti tak ho tímhle způsobem můžu nakreslit taky.
Nebo chci přidat funkci pro obvod. Ve tvém případě musím tu funkci dodat do interface a doimplementovat ve všech odvozených třídách. Není jednodušší napsat jedinou funkci, která spočítá obvod rovnoběžníka a v případě čtverce využívá ty "zbytečné" počítané vlastnosti?
Jak uz od zacatku tady nekdo rikal, vsechno zavisi na zpusobu pouziti.
Tyhle zobecnovani, co tady delas zas vedou tomu, ze casto to co kompl zvladl pred 20 lety s tehdejsim hw, dela dneska na 20 let novejsim hw stejnou dobu. Kdyz budes mit miliardy ctvercu a budes je nekde muset skladovat a budes potrebovat pocitat jejich obsahy, tak si sakra rozmyslis, jestli tam budes nekde pocitat cos, drzet si zbytecne data, atp.
Odtud pak vznikaji napriklad tragicke aplikace v Jave - nepouzitelny lagovaci moloch, prestoze Java sama o sobe umi byt i relativne hodne rychla.
P.S. sizeof(Parallelogram) = strideof(Parallelogram) = 24 a světe div se, sizeof(Square) = strideof(Square) = 8
-
Nezapomínej, že to rozdělení musí být logické, ne adminstrativní.
Logicke rozdeleni to je z pohledu vykonu - pri kazde zmene chunku (napr. zniceni bloku) je treba znovu vytvorit mesh (3d "model" chunku, rendrovat blok po bloku to opravdu nejde, vytvori se jen model s viditelnymi stranami bloku), coz je draha operace a pri nevhodne velikosti to povede k vykonostnim problemum (velky chunk -> casteji a dlouho se generuje, maly chunk -> vice rezie).
A klidně to můžeš dělat i během života (postaví zeď atd).
Po pravde, az takto dynamicke rozdelovani sveta zni velmi narocne. Kdyz si vezmu, ze jen hloupy algoritmus pro svetlo (trivialni flood-fill, navic s podstatne nizsim polomerem nez je napr. dostrel luku) pusobi vykonostni problemy.
Chyba v úvaze je v tom, že předpokládáš, že ten objekt něco renderuje.
Logické rozdělení je to z pohledu toho, zda se mohou elementy ovlivňovat.
Zkus si představit jednoduché lineární pole objektů (pańáců a pomerančů). Pak můžeš mět jakýsi strom, který určuje, kde se které větve mohou ovlivňovat. Sourozence můžeš paralelizovat (když si to čtu, tak to zní děsivě, ale popisuji spíše potenciál, protože v praxi by to byl spíš takovej keř). A v každém ticku se ti v tom stromu provedou výpočty a v kořeni ti vypadne seznam všech změn, které musíš provést na scéně. Až tady máš to místo, kde budeš renderovat. A kde můžeš perfektně optimalizovat (zničení chunku, a vygenerování už finálního se všemi elementy, renderování jen viditelné scény, atd). A díky tomu, že mám seznam všech změn, mohu zkusit paralelizovat i to renderování.
Jak by se to dalo dělat lépe? JS navrhoval Monády, ale (pravděpodobně díky nedostatku zkušeností) bych se bál aby mi z toho nevylezl podobnej nepřehlednej mrdník, jaký obhajuje gamer u svého mutable řešení.
-
Řekněme, že chci mít objekty čtverec, obdélník, kosočtverec a rovnoběžník. Je jasné, že s jednoduchou dědičností se daleko nedostanu. Proto obecný dotaz: existuje nějaké ... ?
... Omezená dědičnost (je něco lepšího než OOP?)
Doufám, že jste tazatelovi pomohli ...
Vzhledem k obecnosti otázky celkem jo, až na to zaplevelení diskusí o FP ve hrách.
Tak při vší úctě, relevantnější by bylo (alespoň pro mne) vyjádření tazatele.
-
Řekněme, že chci mít objekty čtverec, obdélník, kosočtverec a rovnoběžník. Je jasné, že s jednoduchou dědičností se daleko nedostanu. Proto obecný dotaz: existuje nějaké ... ?
... Omezená dědičnost (je něco lepšího než OOP?)
Doufám, že jste tazatelovi pomohli ...
Vzhledem k obecnosti otázky celkem jo, až na to zaplevelení diskusí o FP ve hrách.
Tak při vší úctě, relevantnější by bylo (alespoň pro mne) vyjádření tazatele.
No to je právě ono - už se, pokud mi to nějak neuniklo, neozval, tudíž ten thread už dávno není jeho.
-
Jak uz od zacatku tady nekdo rikal, vsechno zavisi na zpusobu pouziti.
Tyhle zobecnovani, co tady delas zas vedou tomu, ze casto to co kompl zvladl pred 20 lety s tehdejsim hw, dela dneska na 20 let novejsim hw stejnou dobu. Kdyz budes mit miliardy ctvercu a budes je nekde muset skladovat a budes potrebovat pocitat jejich obsahy, tak si sakra rozmyslis, jestli tam budes nekde pocitat cos, drzet si zbytecne data, atp.
Já znám protokoly primárně z Haskellu. Nevím jak jinde ale v Haskellu tu potřebnou tabulku funkcí hlídá a předává překladač. A taky ty funkce ochotně inlinuje všude, kde to jenom jde. Takže pokud budu mít miliardy čtverců a budu si jistý tím že to jsou čtverce, tak mi překladač zainlinuje volání a kosinus vyoptimalizuje pryč.
Zrovna protokoly jsou abstrakce, která umí být překvapivě levná. Teda aspoň v příčetně navrženém jazyce.
Ha, tak Swift se zdá být naprosto příčetný, pro Array<Square> je zrychlení výpočtu skoro o dva řády.
-
Chyba v úvaze je v tom, že předpokládáš, že ten objekt něco renderuje.
Logické rozdělení je to z pohledu toho, zda se mohou elementy ovlivňovat.
Bohuzel pro rychle vytvoreni modelu (meshe) je myslim nutna tato reprezentace (ikdyz sam se nerendruje, je rendrovan). Uzivaji se co nejefektivnejsi datove typy (tusim pole id bloku - primitivnich intu; overhead pri pouziti napr. Map[(Int,Int,Int),Int] by byl primo monstrozni). Je take treba si uvedomit narocnost voxeloveho sveta - std. dohlednost je IIRC 10, tzn. 20x20chunku = 400x16^2x256 bloku = ~26 milionu bloku (kazdy blok az 6 sten ruzne textury a osvetleni). To je nutne efektivne skladovat, jinak dojde pamet velmi rychle (zanedbal jsem napr. osvetleni, sky flag, tile entity a urcite dalsi veci).
Zkus si představit jednoduché lineární pole objektů (pańáců a pomerančů). Pak můžeš mět jakýsi strom, který určuje, kde se které větve mohou ovlivňovat. Sourozence můžeš paralelizovat (když si to čtu, tak to zní děsivě, ale popisuji spíše potenciál, protože v praxi by to byl spíš takovej keř). A v každém ticku se ti v tom stromu provedou výpočty a v kořeni ti vypadne seznam všech změn, které musíš provést na scéně. Až tady máš to místo, kde budeš renderovat. A kde můžeš perfektně optimalizovat (zničení chunku, a vygenerování už finálního se všemi elementy, renderování jen viditelné scény, atd). A díky tomu, že mám seznam všech změn, mohu zkusit paralelizovat i to renderování.
Na entitach si to celkem predstavit jde (je jich pouze par, v pripade MC tak do stovky v okruhu viditelnosti), ale v pripade bloku? Mit stromy i pro bloky, napr. pro kazdy blok lavy (blok vyskytujici se v "prirode", tj. muze byt ve velkem mnozstvi soucasti sveta), ktery muze zapalit horlave objekty ve svem okoli, bude vetev v tom stromu? To bysme se posunuli o rad, mozna o dva. V kazdem ticku se muze lava "rozlit" dale (zmen za tick u lavovych jezer muze byt tedy mnoho), opravdu si nedovedu predstavit, ze by generovani toho stromu trvalo kratsi dobu, nez neparalelni puvodni verze. Kdyz vidim, jake vykonostni problemy jsou v aktualnim MC obcas s tokem vody, lavy a prepocty osvetleni (kteremu ta tvorba stromu bude asi celkem blizko), tak mi prijde, ze tohle reseni nemuze fungovat (ale mozna to jen klame, fakt nevim).
V zaveru mi z toho tedy vyplyva, ze by musela zustat stavajici reprezentace (pouzivat strom pro meshovani sveta si myslim rozumne nepujde) a paralelne udrzovat strom ovlivnitelnosti. Pomoci nej paralelizovat tick a z vysledneho seznamu zmen paralelne zpracovat vsechny chunky zaraz (prikazy pro 1 chunk tedy budou sekvencni, coz nevadi, maloktere (?) pouzivane cpu ma vice nez 400 jader). Je otazka, zda rezie spjata s udrzovanim/generovanim stromu a rozdelovanim prace (seznam zmen) nevyjde hur, nez stary jednodussi pristup ??? (tyto zmeny se budou bezne provadet klidne nekolikrat do vteriny, pokud budou trvat dele nez jeden frame, tak se hra zacne "skubat"; anebo by se muselo zacit resit nejaky "shadow world" ala "shadow dom", to ale nevim, jestli by pametove fungovalo).
Jak by se to dalo dělat lépe? JS navrhoval Monády, ale (pravděpodobně díky nedostatku zkušeností) bych se bál aby mi z toho nevylezl podobnej nepřehlednej mrdník, jaký obhajuje gamer u svého mutable řešení.
Prijde mi, ze tema programovani her je velmi narocne - musi se zajistit velmi dobry vykon, pritom ale stabilni (aby nekolisalo FPS). U voxelovych her je vse jeste umocneno nepodporou enginu i GPU. Pritom zrovna ty voxelove hry jsou myslim primo idealni pro masivni paralelizaci (a tedy i pro FP?).
Omlouvam se za dlouhy post. Doufam, ze jsem nektere veci moc nezjednodusil nebo spatne nepochopil. Jen dodam, ze jsem napsal velmi jednoduche rendrovani voxeloveho sveta v jME a od te doby jsem zacal mit respekt k Minecraftu a jeho (ne)dobremu vykonu ;D.
-
Uzivaji se co nejefektivnejsi datove typy (tusim pole id bloku - primitivnich intu; overhead pri pouziti napr. Map[(Int,Int,Int),Int] by byl primo monstrozni).
Tady se moc nechytám co přesně myslíš. Navíc mám nezdravou důvěru v optimalizátor GHC :-) Asi by to chtělo konkrétní příklad.
Na entitach si to celkem predstavit jde (je jich pouze par, v pripade MC tak do stovky v okruhu viditelnosti), ale v pripade bloku? Mit stromy i pro bloky, napr. pro kazdy blok lavy (blok vyskytujici se v "prirode", tj. muze byt ve velkem mnozstvi soucasti sveta), ...
Mluvím-li o entitách, tak mluvím i o neživých věcech. Jako entitu jsem měl cokoliv, co mohlo reagovat s okolím. Aktivně, nebo jen klást odpor.
V zaveru mi z toho tedy vyplyva, ze by musela zustat stavajici reprezentace (pouzivat strom pro meshovani sveta si myslim rozumne nepujde) a paralelne udrzovat strom ovlivnitelnosti. Pomoci nej paralelizovat tick a z vysledneho seznamu zmen paralelne zpracovat vsechny chunky zaraz (prikazy pro 1 chunk tedy budou sekvencni, coz nevadi, maloktere (?) pouzivane cpu ma vice nez 400 jader). Je otazka, zda rezie spjata s udrzovanim/generovanim stromu a rozdelovanim prace (seznam zmen) nevyjde hur, nez stary jednodussi pristup ??? (tyto zmeny se budou bezne provadet klidne nekolikrat do vteriny, pokud budou trvat dele nez jeden frame, tak se hra zacne "skubat"; anebo by se muselo zacit resit nejaky "shadow world" ala "shadow dom", to ale nevim, jestli by pametove fungovalo).
Možná si jen nerozumíme, protože mi přijde, že popisuješ to co jsem měl na mysli já. Plus-mínus :-)
-
První je analogií imperativního přístupu, pokaždé když nastavím nový stav objektu, tak vyrobím celý nový "správný" svět. To bude fungovat naprosto stejně jako imperativní přístup. Má to ale svoje ale. I kdyby výroba nového světa nestála žádný výkon navíc (čemuž nevěřím), tak je takový design silně diskutabilní. Mám prakticky netestovatelné funkce, když funkce vrací celý svět, může potenciálně změnit cokoliv. Vůbec nemám přehled o datech, která do funkcí vstupují a které funkce vrací, z interface to není vidět.
Druhý způsob je nedělat změny ve světě ihned, ale schovat si je na později. Vytvořím seznam událostí, co se má se světem stát. Problém tohoto přístupu je v tom, že dává chybné výsledky a nekonzistentní svět. Když počítám všechno z původního světa, můžou se rozhodnout panácí 1 i 2, že oba vstoupí na pole A. Teď co s tím? Nechám panáka 2 tam, kde je, nebo spustím ex-post znovu AI a najdu pro panáka 2 novou pozici? Obě řešení jsou špatná. Hráč očekává, že panák 2 něco udělá a ne že bude občas "zasekávat". Spouštět znovu AI je nekoncepční a stojí to výkon. I když jde o naprosto elemetární příklad, tak stejně nevím, co s tou kolizí udělat. V reálně hře budou tisíce různých událostí a miliony jejich možných vzájemných kombinací. Já nemám dost dobrý mozek na to, abych dokázal domyslet, jak řešit všechny možné kolize. Nevybral bych si toto řešení ani náhodou. Z mého pohledu je nedeterministické.
A víte co je zajímavé, že rálný svět vykazuje oba tyto přístupy, makrosvět je to první řešení a kvantový mikrosvět to druhé :-)
Zvolte rozsahy možných kolizí a náhodně vyberte výsledek z toho rozsahu. Logické kolize do makrosvěta hry nezasáhnou, když kolem elementárních prvků definujete okolí, kde interakce probíhají, přenos interakce pak bude možný jen tehdy, když bude existovat řetěz elementárních prvků, či událostí, které se vzájemně budou dotýkat v rámci takto definovaného okolí :-)))
-
...
A víte co je zajímavé, že rálný svět vykazuje oba tyto přístupy, makrosvět je to první řešení a kvantový mikrosvět to druhé :-)
Zvolte rozsahy možných kolizí a náhodně vyberte výsledek z toho rozsahu. Logické kolize do makrosvěta hry nezasáhnou, když kolem elementárních prvků definujete okolí, kde interakce probíhají, přenos interakce pak bude možný jen tehdy, když bude existovat řetěz elementárních prvků, či událostí, které se vzájemně budou dotýkat v rámci takto definovaného okolí :-)))
A o všemocném, všezachraňujícím a všeřešícím trhu nic?
Nic??
NIC???
-
Uzivaji se co nejefektivnejsi datove typy (tusim pole id bloku - primitivnich intu; overhead pri pouziti napr. Map[(Int,Int,Int),Int] by byl primo monstrozni).
Tady se moc nechytám co přesně myslíš. Navíc mám nezdravou důvěru v optimalizátor GHC :-) Asi by to chtělo konkrétní příklad.
Stačí se podívat na Computer Language Benchmarks Game. Například problém n-body je v Haskellu řešen pomocí těchto konstrukcí:
offset o = (plusPtr (castPtr p::Ptr Double)(o*8+i*64))
nebo
pa <- mallocBytes (len * sizeOf (undefined::Planet))
Naneštěstí je řešení v Haskellu delší, méně přehledné, méně bezpečné a pomalejší (http://benchmarksgame.alioth.debian.org/u32q/haskell.php) než řešení v Javě.
-
Presne jak pise Radek Miček (ten typ predstavuje mapu [slovnik] ze souradnice [triple intu] na int [id bloku - napr. trava, kamen]; primitivnim intem myslim datovy typ, ktery v JVM IIRC odpovida 64b integeru, ne-primitvni by byl objekt obalujici int [v Jave Integer?]).
Problem je v tom, ze pri uvedenem prikladu s bloky se pocita doslova kazdy bit - "struktura" predstavujici blok (v nasem pripade 64b int, kde cast myslim tvori bitove flagy) - se opakuje ~20 milionkrat (a to se bavime jen o klientovi, server muze mit desitky hracu, tzn. nasobne vice prace). Implementace chunku jako Map[(Int,Int,Int),Int] by znamenala minimalne 300% pametovy overhead (explicitni souradnice) a bylo by to podstatne pomalejsi, protoze pro vyhledavani v teto strukture se budou pouzivat hashe souradnic, prestoze by to mohlo byt v klidu pole intu (mozna ne o tolik pomalejsi, jit optimalizator obcas dela zazraky; navic pri vyhledavani se bude konstruoovat triple podle ktereho se vyhledava). Muj priklad je extrem (mluvim z pohledu JVM, Haskell skoro neznam), nikdo by neco takoveho asi ani nezkousel, ale celkem dobre to ilustruje podstatu.
PS: Prace s ukazateli v Haskellu nevypada vubec vabne (pripadam si jak kdybych cetl C++ kod).
-
kit: "Objektové jazyky na tom bývají hůř, protože když to nezvládnou zoptimalizovat při překladu, tak za běhu to už nedohoní a ten kosinus budou počítat vždy."
To bych se hádal, to jazyky s bytekódem když zjistí že je možná a vhodná "optimalizace za běhu", tak si klidně bytekód přeloží znovu pro konkrétní případ.
===
Jinak celkově debatu nechápu. Porovnávají se tu dvě řešení: s eventy a bez, a jedno z nich "se považuje" za funkcionální. Co komu brání napsat v imperativním přístupu řešení s eventy a získat tím také výhodu změn scény a tedy možnost vzniku kolizí pouze na jednom místě? IMHO ani jedno z prezentovaných řešení nijak nesouvisí s paradigmatem jazyka, v kterém ho budete implementovat.
-
Jinak celkově debatu nechápu. Porovnávají se tu dvě řešení: s eventy a bez, a jedno z nich "se považuje" za funkcionální. Co komu brání napsat v imperativním přístupu řešení s eventy a získat tím také výhodu změn scény a tedy možnost vzniku kolizí pouze na jednom místě? IMHO ani jedno z prezentovaných řešení nijak nesouvisí s paradigmatem jazyka, v kterém ho budete implementovat.
To vzniklo tak, že gamer si myslí, že řešení s eventy je jediné možné a pořád dokola to opakoval, i když byl opakovaně upozorněn, že jediné možné není :)
-
Jinak celkově debatu nechápu. Porovnávají se tu dvě řešení: s eventy a bez, a jedno z nich "se považuje" za funkcionální. Co komu brání napsat v imperativním přístupu řešení s eventy a získat tím také výhodu změn scény a tedy možnost vzniku kolizí pouze na jednom místě? IMHO ani jedno z prezentovaných řešení nijak nesouvisí s paradigmatem jazyka, v kterém ho budete implementovat.
To si myslim nikdo netvrdi, akorat se tak asi nedeje. Pokud jsou vhodnejsi nastroje pro implementaci daneho reseni (eventy) ve funkcionalnich jazycich, tak asi lide vice tihnout k tomu uzivat ty funkc. jazyky pro dane reseni. Priklad: pri psani utilitky v Pythonu jsem se snazil to psat funkcionalne, ale prestoze to jde, tak je to tak krkolomne a neuveritelne uzvanene... Napr. na Scalu [nebo nedej boze Haskell] to proste nema.
No, rekl bych, ze ta druha moznost - mutable data ve funkcionalnim jazyku - nebude moc typicka :D.
PS: Jak pise pan Prymek, neni to jedine reseni. Napr. Akka (knihovna s actory) je dostupna i pro Javu, ktera neni urcite funkcionalni jazyk.
-
Uzivaji se co nejefektivnejsi datove typy (tusim pole id bloku - primitivnich intu; overhead pri pouziti napr. Map[(Int,Int,Int),Int] by byl primo monstrozni).
Tady se moc nechytám co přesně myslíš. Navíc mám nezdravou důvěru v optimalizátor GHC :-) Asi by to chtělo konkrétní příklad.
Stačí se podívat na Computer Language Benchmarks Game. Například problém n-body je v Haskellu řešen pomocí těchto konstrukcí:
offset o = (plusPtr (castPtr p::Ptr Double)(o*8+i*64))
nebo
pa <- mallocBytes (len * sizeOf (undefined::Planet))
Naneštěstí je řešení v Haskellu delší, méně přehledné, méně bezpečné a pomalejší (http://benchmarksgame.alioth.debian.org/u32q/haskell.php) než řešení v Javě.
Nevím, jestli by to v tomto konkrétním případě měnilo situaci, ale vybrat jeden konkrétní algoritmus původně použitý v Javě a pak chtít po implementátorech ve všech možných i nemožných jazycích ten algoritmus používat mi přijde zrovna třeba pro funkcionální jazyky jako dost velký podraz.
-
vybrat jeden konkrétní algoritmus původně použitý v Javě a pak chtít po implementátorech ve všech možných i nemožných jazycích ten algoritmus používat mi přijde zrovna třeba pro funkcionální jazyky jako dost velký podraz.
v CLBG* to není algoritmus, spíš problém, ne? Algoritmy jsou afaik různé. Když už v tom hledat nějaký podraz, tak že jsou to víceméně výpočetní problémy - třeba takový Erlang v nich vyloženě nemá šanci, takže dopadá špatně i když je to jinak skvělý jazyk.
* to je skoro jak LGBT :))
-
v CLBG* to není algoritmus, spíš problém, ne? Algoritmy jsou afaik různé.
Pokud to dobře chápu, tak nikoliv - cituju ze stránky problému n-body:
We are trying to show the performance of various programming language implementations - so we ask that contributed programs not only give the correct result, but also use the same algorithm to calculate that result.
Each program should model the orbits of Jovian planets, using the same simple symplectic-integrator - see the Java program.
Když už v tom hledat nějaký podraz, tak že jsou to víceméně výpočetní problémy - třeba takový Erlang v nich vyloženě nemá šanci, takže dopadá špatně i když je to jinak skvělý jazyk.
No já jsem ani tak neměl na mysli nějaký vědomý podraz nebo konkrétně Haskell (byť poznámka Radka Mička byla validní a Haskellu se důvodně týkala). Jde spíš o to, že pokud se mají vysokoúrovňové jazyky a zvláště FP posunout dál, budou asi muset hledat jiné cesty než kopírovat C či Javu.
-
... Jde spíš o to, že pokud se mají vysokoúrovňové jazyky a zvláště FP posunout dál, budou asi muset hledat jiné cesty než kopírovat C či Javu.
Pusobi to na me tak, ze zrovna na hry by se FP poustet nemelo. Resp. ne do te doby, nez budou optimalizace na urovni stavajicich prekladacu/virtualnich stroju (tzn. ze prekladac bude opravdu spolehlive transformovat funkcionalni veci na imperativni). IMO zacit by se melo tam, kde trochu horsi vykon je vyvazen rychlejsim vyvojem. Velmi podobna pozice jakou mela Java - ma (melo) to maly vykon, ale vyvoj je velmi svizny, takze to vyvazi ty nevyhody: $ za vyvoj >> $ za hw => radeji koupime lepsi/vic hw.
-
Pusobi to na me tak, ze zrovna na hry by se FP poustet nemelo. Resp. ne do te doby, nez budou optimalizace na urovni stavajicich prekladacu/virtualnich stroju (tzn. ze prekladac bude opravdu spolehlive transformovat funkcionalni veci na imperativni). IMO zacit by se melo tam, kde trochu horsi vykon je vyvazen rychlejsim vyvojem. Velmi podobna pozice jakou mela Java - ma (melo) to maly vykon, ale vyvoj je velmi svizny, takze to vyvazi ty nevyhody: $ za vyvoj >> $ za hw => radeji koupime lepsi/vic hw.
Problém není v převodu funkcionálního na imperativní kód. To už překladače zvládají poměrně dobře. Problém je převod imperativních programátorů na funkcionální, stačí si přečíst místní diskuzi. Většina programátorů je bez měnitelného stavu totálně ztracená a z toho se odvíjí zbytek. Funkcionální řešení nevymyslí a pokud ho někdo vymyslí za ně, tak ho stejně nepoberou. Naučit se myslet jinak je nadlouho.
A pak je tady taky drobnější problém s tím, že většína existujících nástrojů staví na imperativním přístupu. Setrvačnost udělá hodně.
-
Pusobi to na me tak, ze zrovna na hry by se FP poustet nemelo. Resp. ne do te doby, nez budou optimalizace na urovni stavajicich prekladacu/virtualnich stroju (tzn. ze prekladac bude opravdu spolehlive transformovat funkcionalni veci na imperativni). IMO zacit by se melo tam, kde trochu horsi vykon je vyvazen rychlejsim vyvojem. Velmi podobna pozice jakou mela Java - ma (melo) to maly vykon, ale vyvoj je velmi svizny, takze to vyvazi ty nevyhody: $ za vyvoj >> $ za hw => radeji koupime lepsi/vic hw.
Problém není v převodu funkcionálního na imperativní kód. To už překladače zvládají poměrně dobře. Problém je převod imperativních programátorů na funkcionální, stačí si přečíst místní diskuzi. Většina programátorů je bez měnitelného stavu totálně ztracená a z toho se odvíjí zbytek. Funkcionální řešení nevymyslí a pokud ho někdo vymyslí za ně, tak ho stejně nepoberou. Naučit se myslet jinak je nadlouho.
A pak je tady taky drobnější problém s tím, že většína existujících nástrojů staví na imperativním přístupu. Setrvačnost udělá hodně.
proč by ale měli programovat funkcionálně? co tím získají? viz benchmark
-
proč by ale měli programovat funkcionálně? co tím získají? viz benchmark
Ten benchmark moc nevypovídá o ničem z reálného světa. Vypovídá o tom, o čem vypovídá - takže např. pokud chceš počítat fraktály, je Haskell super! A fakt chceš počítat fraktály? ;)
Co získají? V některých doménách, kam FP sedne jak prcka na nočník*, můžou získat hodně: přehledný, udržovatelný, čistý kód s malými nároky na hw a na množství programátorů. To není málo. Ale náklady jsou taky velké: nedostatek (-> cena) lidí, mladé nástroje, nevyzrálý ekosystém, u některých jazyků neexistující komerční podpora. Jestli u konkrétního projektu výhody převýší nebo nepřevýší náklady, to si holt už každý musí rozmyslet sám. Podle mě se FP může docela dobře hodit pro malý tým dobrých programátorů - dobře si to promyslí a rychle se dostanou k cíli, neztrácí čas balastem.
* co to všechno je, to ať si každý posoudí sám. Podle mě by do toho mohlo spadat víceméně všechno, kde jde o nějaké streamové a transakční zpracování, plus některé věci event-driven. Počet těchto domén bude imho narůstat - všechny ty internety, IoTy a tak...
-
Pokud to dobře chápu, tak nikoliv - cituju ze stránky problému n-body:
We are trying to show the performance of various programming language implementations - so we ask that contributed programs not only give the correct result, but also use the same algorithm to calculate that result.
Each program should model the orbits of Jovian planets, using the same simple symplectic-integrator - see the Java program.
Aha, díky. Tak buď žiju v omylu, nebo je tenhle jeden problém výjimka :)
Jde spíš o to, že pokud se mají vysokoúrovňové jazyky a zvláště FP posunout dál, budou asi muset hledat jiné cesty než kopírovat C či Javu.
Zatím to vypadá spíš tak, že mainstreamové jazyky se snaží do sebe FP nějak aspoň zčásti vcucnout. Kdyby to dopadlo tak, že by se třeba z F# stal jazyk, ve kterém by se dalo slušné psát, měl by širší základnu a slušnou podporu, takže by nikdo o Haskell, Erlang atd. zájem neměl, tak bych se třeba já ani moc nezlobil. Pořád lepší než drátem do voka dominance C++ ;)
Spíš se ale trochu bojím toho, že kvůli technickým a manažerským omezením se FP do mainstreamu dostane ve stejně vykastrované podobě jako se to stalo OOP :(
-
Spíš se ale trochu bojím toho, že kvůli technickým a manažerským omezením se FP do mainstreamu dostane ve stejně vykastrované podobě jako se to stalo OOP :(
Nekteri rikaji, ze se Scala stane dalsi Javou (osobne tak optimisticky nejsem, myslim ze je prilis "slozita" pro bezne pohodlne Javisty). Bohuzel kvuli omezenim JVM se ten typovy system rozhodne nemuze rovnat treba Haskellu (a to s nim mam zkusenosti jen z maleho skolniho projektu).
Cim dele si ctu tuto diskuzi (nechce se mi az verit te vyspelosti prekladacu/runtimu), tim vice mam nutkani se navratit ke zkoumani Haskellu po vecerech :D. Nejaky tip na video kurzy/prednasky?
-
proč by ale měli programovat funkcionálně? co tím získají? viz benchmark
Ten benchmark moc nevypovídá o ničem z reálného světa. Vypovídá o tom, o čem vypovídá - takže např. pokud chceš počítat fraktály, je Haskell super! A fakt chceš počítat fraktály? ;)
Co získají? V některých doménách, kam FP sedne jak prcka na nočník*, můžou získat hodně: přehledný, udržovatelný, čistý kód s malými nároky na hw a na množství programátorů. To není málo. Ale náklady jsou taky velké: nedostatek (-> cena) lidí, mladé nástroje, nevyzrálý ekosystém, u některých jazyků neexistující komerční podpora. Jestli u konkrétního projektu výhody převýší nebo nepřevýší náklady, to si holt už každý musí rozmyslet sám. Podle mě se FP může docela dobře hodit pro malý tým dobrých programátorů - dobře si to promyslí a rychle se dostanou k cíli, neztrácí čas balastem.
* co to všechno je, to ať si každý posoudí sám. Podle mě by do toho mohlo spadat víceméně všechno, kde jde o nějaké streamové a transakční zpracování, plus některé věci event-driven. Počet těchto domén bude imho narůstat - všechny ty internety, IoTy a tak...
to asi platí i o LISPu, ty jejich makra jsou prý velmi schopné, IMHO jsme v bezpečí, FP se neprosadí
-
Spíš se ale trochu bojím toho, že kvůli technickým a manažerským omezením se FP do mainstreamu dostane ve stejně vykastrované podobě jako se to stalo OOP :(
Nekteri rikaji, ze se Scala stane dalsi Javou (osobne tak optimisticky nejsem, myslim ze je prilis "slozita" pro bezne pohodlne Javisty). Bohuzel kvuli omezenim JVM se ten typovy system rozhodne nemuze rovnat treba Haskellu (a to s nim mam zkusenosti jen z maleho skolniho projektu).
Cim dele si ctu tuto diskuzi (nechce se mi az verit te vyspelosti prekladacu/runtimu), tim vice mam nutkani se navratit ke zkoumani Haskellu po vecerech :D. Nejaky tip na video kurzy/prednasky?
tip na studijní materiály nemám, ale jestli se rozhodnete vyzkoušet Haskell na implementaci překladače, tak začněte knhovnou Parsec, je těžko k uvěření, že to je zadarmo
STM monáda a forkIO je taky docel super
vůbec GHC Haskell je asi nejlepší současný "programovací systém" (krom výpočetně náročných věcí, ale s GPU to prý taky nějak umí)
-
Nejaky tip na video kurzy/prednasky?
Nestačí learnyouahaskell.com + cvičně si něco napsat? :) Videa přímo o Haskellu neporadím (nedělám v něm, jenom mě tak baví se na něho občas kouknout - taky po večerech :) ), ale docela mě baví videa z tohodle channelu: https://www.youtube.com/channel/UC_QIfHvN9auy2CoOdSfMWDw
to asi platí i o LISPu, ty jejich makra jsou prý velmi schopné
Asi jo, ale když já ty závorky prostě nemůžu, krvácí mi z toho oči ;) Pravděpodobně stejně silnej, nebo o něco málo slabší makrosystém má Elixir: http://elixir-lang.org/getting-started/meta/macros.html
-
... mladé nástroje, nevyzrálý ekosystém, ...
https://www.haskell.org/cabal/ (https://www.haskell.org/cabal/) bylo pro mě nádherné mlsání.
-
(krom výpočetně náročných věcí, ale s GPU to prý taky nějak umí)
Co, GPU, ale bindingy Rka! ;) https://plus.google.com/+JohnCook/posts/dp8sN7B4bzS
-
Spíš se ale trochu bojím toho, že kvůli technickým a manažerským omezením se FP do mainstreamu dostane ve stejně vykastrované podobě jako se to stalo OOP :(
Nekteri rikaji, ze se Scala stane dalsi Javou (osobne tak optimisticky nejsem, myslim ze je prilis "slozita" pro bezne pohodlne Javisty). Bohuzel kvuli omezenim JVM se ten typovy system rozhodne nemuze rovnat treba Haskellu (a to s nim mam zkusenosti jen z maleho skolniho projektu).
Cim dele si ctu tuto diskuzi (nechce se mi az verit te vyspelosti prekladacu/runtimu), tim vice mam nutkani se navratit ke zkoumani Haskellu po vecerech :D. Nejaky tip na video kurzy/prednasky?
IMO docela dobrý kurs s online materiály je třeba: http://www.cis.upenn.edu/~cis194/lectures.html
A nejlepší souhrn materiálů co znám je zde: https://gist.github.com/leroux/6395804
-
Spíš se ale trochu bojím toho, že kvůli technickým a manažerským omezením se FP do mainstreamu dostane ve stejně vykastrované podobě jako se to stalo OOP :(
Bohuzel kvuli omezenim JVM se ten typovy system rozhodne nemuze rovnat treba Haskellu.
Nad JVM nebo CLR můžete postavit jazyky se silnějšími typovými systémy než má Haskell.
Ostatně Scala nemá ostře slabší typový systém než Haskell. Na rozdíl od Haskellu má Scala navíc podtypový polymorfismus. Důsledkem toho je slabší typová inference.
-
vůbec GHC Haskell je asi nejlepší současný "programovací systém" (krom výpočetně náročných věcí, ale s GPU to prý taky nějak umí)
Dovolil bych si nesouhlasit: 1) GHC Haskell je poměrně komplikovaný (možná kvůli tomu, aby měl dobrou typovou inferenci) - existuje řada podobných jazyků, které mají silnější typový systém a jsou jednodušší než Haskell - např. Idris (má však horší typovou inferenci). 2) O časové a prostorové složitosti určité části kódu je nutné uvažovat v kontextu celého programu, nestačí uvažovat o dané části izolovaně.
-
vůbec GHC Haskell je asi nejlepší současný "programovací systém" (krom výpočetně náročných věcí, ale s GPU to prý taky nějak umí)
Dovolil bych si nesouhlasit: 1) GHC Haskell je poměrně komplikovaný (možná kvůli tomu, aby měl dobrou typovou inferenci) - existuje řada podobných jazyků, které mají silnější typový systém a jsou jednodušší než Haskell - např. Idris (má však horší typovou inferenci). 2) O časové a prostorové složitosti určité části kódu je nutné uvažovat v kontextu celého programu, nestačí uvažovat o dané části izolovaně.
já jsem se samozřejmě nevyjádřil moc precizně, měl jsem na mysli praktické použití, knihovny, komunitu
-
asi naprostý offtopic, ale velicr zajímavé čtení http://aosabook.org/en/ghc.html
ty počty řádků...
-
...
A víte co je zajímavé, že rálný svět vykazuje oba tyto přístupy, makrosvět je to první řešení a kvantový mikrosvět to druhé :-)
Zvolte rozsahy možných kolizí a náhodně vyberte výsledek z toho rozsahu. Logické kolize do makrosvěta hry nezasáhnou, když kolem elementárních prvků definujete okolí, kde interakce probíhají, přenos interakce pak bude možný jen tehdy, když bude existovat řetěz elementárních prvků, či událostí, které se vzájemně budou dotýkat v rámci takto definovaného okolí :-)))
A o všemocném, všezachraňujícím a všeřešícím trhu nic?
Nic??
NIC???
To je přece fyzikální implementace trhu :-) V toto prostředí se šíří jen dostatečně silné signály, které mají takovou energii, která je schopná ovlivnit více elementárních částic. A to je přesně funkce trhu. Selektovat vznikající signály a tak optimalizovat rozdělování zdrojů.
Vyjdete-li z tohoto principu zjistíte, že stav modelového světa hry nemusíte udržovat fyzicky v nějakých datových strukturách, ale můžete pouze pracovat s jeho ideou a vybírat náhodně z možných lokálních interakcí, jejich propagace do makrosvěta hry, vytvoří iluzi příčiny a následku, stejně jak se to děje v našem "reálném" světě :-)))
-
asi naprostý offtopic, ale velicr zajímavé čtení http://aosabook.org/en/ghc.html
ty počty řádků...
Zajimave. A je to hodne nebo malo? Napadlo me srovnani - ZIL - interpretr a optimalizujici kompilator varianty Lispu (vzhledem k dobe vzniku to nemohl byt Common Lisp) pro IBM Mainframe (OS/370), vcetne "standardni" knihovny - zhruba 40k radek assembleru a 40k radek Lispu. Jestli sem nekdo hodi cisla pro jine kompilatory/systemy, rad si je poslechnu.
-
Nad JVM nebo CLR můžete postavit jazyky se silnějšími typovými systémy než má Haskell.
Ostatně Scala nemá ostře slabší typový systém než Haskell. Na rozdíl od Haskellu má Scala navíc podtypový polymorfismus. Důsledkem toho je slabší typová inference.
Mel jsem na mysli predevsim generiku a inferenci. A to, ze to "jde" postavit, neznamena, ze by se melo - vykon muze byt dost zalostny, pokud se bude muset uzivat ve velkem reflexe protoze omezeni JVM (ikdyz myslim celkem dost toho padlo, nekde jsem cetl neco s dynamic instrukcemi).
Generika JVM je dost chaba. Ve Scale se musi pouzivat berlicky v podobe tagu a v podstate simulovat runtime podporu generiky pomoci reflexe. CLR je o uroven vys.
No, je mozne ze je to dusledek toho podtypoveho polymorfismu (netusim), ale faktem je, ze oproti Haskellu se musi Scale opravdu casto pomahat (jiste, porad lepsi nez Java).
-
...
A víte co je zajímavé, že rálný svět vykazuje oba tyto přístupy, makrosvět je to první řešení a kvantový mikrosvět to druhé :-)
Zvolte rozsahy možných kolizí a náhodně vyberte výsledek z toho rozsahu. Logické kolize do makrosvěta hry nezasáhnou, když kolem elementárních prvků definujete okolí, kde interakce probíhají, přenos interakce pak bude možný jen tehdy, když bude existovat řetěz elementárních prvků, či událostí, které se vzájemně budou dotýkat v rámci takto definovaného okolí :-)))
A o všemocném, všezachraňujícím a všeřešícím trhu nic?
Nic??
NIC???
To je přece fyzikální implementace trhu :-) V toto prostředí se šíří jen dostatečně silné signály, které mají takovou energii, která je schopná ovlivnit více elementárních částic. A to je přesně funkce trhu. Selektovat vznikající signály a tak optimalizovat rozdělování zdrojů.
Vyjdete-li z tohoto principu zjistíte, že stav modelového světa hry nemusíte udržovat fyzicky v nějakých datových strukturách, ale můžete pouze pracovat s jeho ideou a vybírat náhodně z možných lokálních interakcí, jejich propagace do makrosvěta hry, vytvoří iluzi příčiny a následku, stejně jak se to děje v našem "reálném" světě :-)))
Evidentně dost dobrej matroš, prozradíš, čím přihnojuješ?
-
asi naprostý offtopic, ale velicr zajímavé čtení http://aosabook.org/en/ghc.html
ty počty řádků...
Pro srovnání - OCaml, současný stav:
lexer (lex) - 3277
parser (parsing) - 10029
typechecker (typing) - 33468
překlad do nativního kódu (asmcomp) - 24787
běhové prostředí pro nativní kód (asmrun) - 7448
překlad do bajtkódu (bytecomp) - 13707
běhové prostředí pro bajtkód (byterun) - 22537
REPL (toplevel) - 3230
standardní knihovna (stdlib) - 24110
sdílený kód (utils) - 2477
celkem: 145070
Nezapočítal jsem například: debugger pro bajtkód (pro nativní kód se používá GDB), ocamlbuild (build systém), ocamldoc (systém pro generování dokumentace), další knihovny (otherlibs), yacc a pár dalších věcí
-
Pusobi to na me tak, ze zrovna na hry by se FP poustet nemelo. Resp. ne do te doby, nez budou optimalizace na urovni stavajicich prekladacu/virtualnich stroju (tzn. ze prekladac bude opravdu spolehlive transformovat funkcionalni veci na imperativni). IMO zacit by se melo tam, kde trochu horsi vykon je vyvazen rychlejsim vyvojem. Velmi podobna pozice jakou mela Java - ma (melo) to maly vykon, ale vyvoj je velmi svizny, takze to vyvazi ty nevyhody: $ za vyvoj >> $ za hw => radeji koupime lepsi/vic hw.
Problém není v převodu funkcionálního na imperativní kód. To už překladače zvládají poměrně dobře. Problém je převod imperativních programátorů na funkcionální, stačí si přečíst místní diskuzi. Většina programátorů je bez měnitelného stavu totálně ztracená a z toho se odvíjí zbytek. Funkcionální řešení nevymyslí a pokud ho někdo vymyslí za ně, tak ho stejně nepoberou. Naučit se myslet jinak je nadlouho.
A pak je tady taky drobnější problém s tím, že většína existujících nástrojů staví na imperativním přístupu. Setrvačnost udělá hodně.
To je podstata problému. Na vině je funkcionální notace a na ní naroubovaná imperativní notace jako if, while a podobně. Pokud někdo objeví notaci, která bude vypadat přirozeně, FP se rozšíří.
Naděje vkládané do FP, ale mohou být liché, protože se v této technologii nerealizují a hlavně neudržují rozsáhlé reálné projekty běžící desítky let. Každý program do 100 řádků vypadá elegantně a je krásný. Horší je to, když už má těch řádků více. Vyjadřovací schopnosti programovacích jazyků nejsou na úrovni textu prózy, ale na úropvni čtení básní, proto i struktura textu je důležitá. A FP jazyky ji mají přímo katastrofální.
-
... A FP jazyky ji mají přímo katastrofální.
citation needed
-
asi naprostý offtopic, ale velicr zajímavé čtení http://aosabook.org/en/ghc.html
ty počty řádků...
Pro srovnání - OCaml, současný stav:
lexer (lex) - 3277
parser (parsing) - 10029
typechecker (typing) - 33468
překlad do nativního kódu (asmcomp) - 24787
běhové prostředí pro nativní kód (asmrun) - 7448
překlad do bajtkódu (bytecomp) - 13707
běhové prostředí pro bajtkód (byterun) - 22537
REPL (toplevel) - 3230
standardní knihovna (stdlib) - 24110
sdílený kód (utils) - 2477
celkem: 145070
Nezapočítal jsem například: debugger pro bajtkód (pro nativní kód se používá GDB), ocamlbuild (build systém), ocamldoc (systém pro generování dokumentace), další knihovny (otherlibs), yacc a pár dalších věcí
docela by mě zajímaly jiné "jednojazykové" překladače, třeba javac nebo cpython, našel jsem údaj pro gcc, ale to je hodně nefér srovnání
-
...
A víte co je zajímavé, že rálný svět vykazuje oba tyto přístupy, makrosvět je to první řešení a kvantový mikrosvět to druhé :-)
Zvolte rozsahy možných kolizí a náhodně vyberte výsledek z toho rozsahu. Logické kolize do makrosvěta hry nezasáhnou, když kolem elementárních prvků definujete okolí, kde interakce probíhají, přenos interakce pak bude možný jen tehdy, když bude existovat řetěz elementárních prvků, či událostí, které se vzájemně budou dotýkat v rámci takto definovaného okolí :-)))
A o všemocném, všezachraňujícím a všeřešícím trhu nic?
Nic??
NIC???
To je přece fyzikální implementace trhu :-) V toto prostředí se šíří jen dostatečně silné signály, které mají takovou energii, která je schopná ovlivnit více elementárních částic. A to je přesně funkce trhu. Selektovat vznikající signály a tak optimalizovat rozdělování zdrojů.
Vyjdete-li z tohoto principu zjistíte, že stav modelového světa hry nemusíte udržovat fyzicky v nějakých datových strukturách, ale můžete pouze pracovat s jeho ideou a vybírat náhodně z možných lokálních interakcí, jejich propagace do makrosvěta hry, vytvoří iluzi příčiny a následku, stejně jak se to děje v našem "reálném" světě :-)))
Evidentně dost dobrej matroš, prozradíš, čím přihnojuješ?
No je to jen obrácení Lemova principu, že počítač s nekonečně velkou pamětí může mít program nulové délky :-))) No a dle některých fyziků takovým počítačem je vlastně reálný svět, když v časoprostoru všechny možné události existují "současně", mimo čas. Abyste zjistil, zda se dvě kružnice protínají, nemusíte porovnávat dva seznamy se souřadnicemi bodů vytvářejících kružnice.
-
Vyjadřovací schopnosti programovacích jazyků nejsou na úrovni textu prózy, ale na úropvni čtení básní, proto i struktura textu je důležitá. A FP jazyky ji mají přímo katastrofální.
Zrovna napr. takovy Haskell dost lidi povazuje za prekrasny jazyk - lze strucne a vystizne zapsat slozite myslenky. Samozrejme pokud si nekdo pod hezkym kodem programu predstavuje co nejvice nevyznamoveho balastu (Java/Python), pak je IMO neco spatne.
-
... A FP jazyky ji mají přímo katastrofální.
citation needed
Stačí se podívat na pár desítek řádků FP kódu. K čemu citace, k tomu stačí vlastní úsudek.
-
Nad JVM nebo CLR můžete postavit jazyky se silnějšími typovými systémy než má Haskell.
Ostatně Scala nemá ostře slabší typový systém než Haskell. Na rozdíl od Haskellu má Scala navíc podtypový polymorfismus. Důsledkem toho je slabší typová inference.
Mel jsem na mysli predevsim generiku a inferenci. A to, ze to "jde" postavit, neznamena, ze by se melo - vykon muze byt dost zalostny, pokud se bude muset uzivat ve velkem reflexe protoze omezeni JVM (ikdyz myslim celkem dost toho padlo, nekde jsem cetl neco s dynamic instrukcemi).
Pokud nepožadujete, aby vygenerovaný kód šel jednoduše použít z Javy, tak můžete v některých případech provést monomorfizaci - tj. generika specializovat (jako v C++; viz Whole-Program Compilation in MLton (http://www.mlton.org/guide/20130715/References.attachments/060916-mlton.pdf#12), slajd 12). Drobný háček je v tom, že to nemusí vždy jít - například, pokud program používá polymorfní rekurzi. Další háček je v tom, že vzniknou problémy s variancí, pokud používáte podtypový polymorfismus - (např. Iterable<int> nebude podtyp Iterable<Object>, zatímco Iterable<Int> je).
-
To je podstata problému. Na vině je funkcionální notace a na ní naroubovaná imperativní notace jako if, while a podobně. Pokud někdo objeví notaci, která bude vypadat přirozeně, FP se rozšíří.
Tomuhle nerozumím, jaké "naroubované if, while"?! Co je na existující notaci špatného? A co je na ní "nepřirozeného"? Například zápis funkcí jako samostatných entit pomocí pattern matchingu mi teda přijde tisíckrát srozumitelnější než různě vnořené ify a smyčky...
Naděje vkládané do FP, ale mohou být liché, protože se v této technologii nerealizují a hlavně neudržují rozsáhlé reálné projekty běžící desítky let. Každý program do 100 řádků vypadá elegantně a je krásný. Horší je to, když už má těch řádků více. Vyjadřovací schopnosti programovacích jazyků nejsou na úrovni textu prózy, ale na úropvni čtení básní, proto i struktura textu je důležitá. A FP jazyky ji mají přímo katastrofální.
Tak to třeba pro Erlang není vůbec pravda.
-
... A FP jazyky ji mají přímo katastrofální.
citation needed
Stačí se podívat na pár desítek řádků FP kódu. K čemu citace, k tomu stačí vlastní úsudek.
viděl jsem jich desetitisíce a nesouhlasím s vámi, zkuste nevydávat dojmy za fakta
-
Vyjadřovací schopnosti programovacích jazyků nejsou na úrovni textu prózy, ale na úropvni čtení básní, proto i struktura textu je důležitá. A FP jazyky ji mají přímo katastrofální.
Zrovna napr. takovy Haskell dost lidi povazuje za prekrasny jazyk - lze strucne a vystizne zapsat slozite myslenky. Samozrejme pokud si nekdo pod hezkym kodem programu predstavuje co nejvice nevyznamoveho balastu (Java/Python), pak je IMO neco spatne.
na druhou stranu, !#$%&*+./<=>?@ je v haskellu validní identifikátor operátoru
-
Vyjadřovací schopnosti programovacích jazyků nejsou na úrovni textu prózy, ale na úropvni čtení básní, proto i struktura textu je důležitá. A FP jazyky ji mají přímo katastrofální.
Zrovna napr. takovy Haskell dost lidi povazuje za prekrasny jazyk - lze strucne a vystizne zapsat slozite myslenky. Samozrejme pokud si nekdo pod hezkym kodem programu predstavuje co nejvice nevyznamoveho balastu (Java/Python), pak je IMO neco spatne.
na druhou stranu, !#$%&*+./<=>?@ je v haskellu validní identifikátor operátoru
A hkjfsdlhgskdjlh3weu5r983w je validni jmeno skoro vsude ;)
-
na druhou stranu, !#$%&*+./<=>?@ je v haskellu validní identifikátor operátoru
A cmfghenrtveqtklwmebwfdmgyfwvbelbnvuhmwdfrlyntwgmfrdyfvjhwr je validní jméno snad ve všech existujících jazycích. Má smysl nějak omezovat kreativitu u operátorů, když u jmen to nikoho ani nenapadne? Vždyť to neomezili ani v Javě :) Napadá tě nějaký příčetný způsob, jak by se dala omezit nechtěná kreativita u libovolných identifikátorů?
-
na druhou stranu, !#$%&*+./<=>?@ je v haskellu validní identifikátor operátoru
A cmfghenrtveqtklwmebwfdmgyfwvbelbnvuhmwdfrlyntwgmfrdyfvjhwr je validní jméno snad ve všech existujících jazycích. Má smysl nějak omezovat kreativitu u operátorů, když u jmen to nikoho ani nenapadne? Vždyť to neomezili ani v Javě :) Napadá tě nějaký příčetný způsob, jak by se dala omezit nechtěná kreativita u libovolných identifikátorů?
tohle je samozřejmě validní námitka, můžu jenom dodat, že alfanumerické identifikátory neprovokují kreativitu tak jako přílišná volnost u operátorů, je to vidět na všelijakých EDSL (sám se taky občas neudržím)
-
Vyjadřovací schopnosti programovacích jazyků nejsou na úrovni textu prózy, ale na úropvni čtení básní, proto i struktura textu je důležitá. A FP jazyky ji mají přímo katastrofální.
Zrovna napr. takovy Haskell dost lidi povazuje za prekrasny jazyk - lze strucne a vystizne zapsat slozite myslenky. Samozrejme pokud si nekdo pod hezkym kodem programu predstavuje co nejvice nevyznamoveho balastu (Java/Python), pak je IMO neco spatne.
Složité myšlenky se špatně udržují, proto se do reálné praxe nehodí. I reálné algoritmy v živé přírodě, které vznikly díky evoluci, nejsou ve své podstatě složité a jsou složeny z interakce jednoduchých elementů. A nejen tam. Například úplný soubor logických funkcí tvoří Shefferova funkce. No až v Haskellu nebude if a nebo while, tedy imperativní prvky, pak možná bude čitelný a elegantní :-) Zaměnit if a : y za y = if a : c moc elegantní není. Z hlediska čitelnosti programu je to katastrofa, protože se vám tam vynořuje to c, a musíte hledat, o co vlastně jde. Jistě u programu o 10 řádcích to problém není, problém to už je, když těch řádků jsou tisíce. Navíc, abyste někdy v budoucnosti mohl modifikovat to c, musíte dojít až k jeho zdroji, a zpětně prohledávat řetězy transformací imutable objektů, protože to c máte samozřejmě použito ve více místech programu a změnit ho potřebujete jen v jednom místě. Přidání modifikačního dekorátoru v místě použití situaci neřeší. Jen systém znepřehledňuje.
-
To je podstata problému. Na vině je funkcionální notace a na ní naroubovaná imperativní notace jako if, while a podobně. Pokud někdo objeví notaci, která bude vypadat přirozeně, FP se rozšíří.
Tomuhle nerozumím, jaké "naroubované if, while"?! Co je na existující notaci špatného? A co je na ní "nepřirozeného"? Například zápis funkcí jako samostatných entit pomocí pattern matchingu mi teda přijde tisíckrát srozumitelnější než různě vnořené ify a smyčky...
Naděje vkládané do FP, ale mohou být liché, protože se v této technologii nerealizují a hlavně neudržují rozsáhlé reálné projekty běžící desítky let. Každý program do 100 řádků vypadá elegantně a je krásný. Horší je to, když už má těch řádků více. Vyjadřovací schopnosti programovacích jazyků nejsou na úrovni textu prózy, ale na úropvni čtení básní, proto i struktura textu je důležitá. A FP jazyky ji mají přímo katastrofální.
Tak to třeba pro Erlang není vůbec pravda.
if a while jsou prvky imperativního programování. Je to stejný prohřešek, jako roubovat OOP do Lispu.
-
if a while jsou prvky imperativního programování. Je to stejný prohřešek, jako roubovat OOP do Lispu.
citation needed
-
Koukam, ze Ivan ma vzdy politicky co? Jasno, soudruzi.
-
if a while jsou prvky imperativního programování. Je to stejný prohřešek, jako roubovat OOP do Lispu.
While se ve FP taky moc nevyskytuje, leda tak něco, co ho hodně vzdáleně připomíná. Funkcionální if (funkce podobná operátoru ?:) není nic roubovaného, ale naprosto plnohodnotný prvek FP.
-
Složité myšlenky se špatně udržují, proto se do reálné praxe nehodí.
Takze misto napr. flatMap a foreach na jeden radek davate prednost ciste imperativnimu pristupu - ukecany cyklus na nekolik radku? Vzdyt to nic neprinasi, jen delsi kod. Nevim, prijde mi, ze slozite myslenky se lepe transformuji na jednoduche ve FP - mam nekolik funkci ktere postupne vstup zpracuji. V imperativnim programovani to casto skonci velkou metodou s nekolika vnorenymi cykly.
I reálné algoritmy v živé přírodě, které vznikly díky evoluci, nejsou ve své podstatě složité a jsou složeny z interakce jednoduchých elementů. A nejen tam. Například úplný soubor logických funkcí tvoří Shefferova funkce. No až v Haskellu nebude if a nebo while, tedy imperativní prvky, pak možná bude čitelný a elegantní :-) Zaměnit if a : y za y = if a : c moc elegantní není. Z hlediska čitelnosti programu je to katastrofa, protože se vám tam vynořuje to c, a musíte hledat, o co vlastně jde. Jistě u programu o 10 řádcích to problém není, problém to už je, když těch řádků jsou tisíce. Navíc, abyste někdy v budoucnosti mohl modifikovat to c, musíte dojít až k jeho zdroji, a zpětně prohledávat řetězy transformací imutable objektů, protože to c máte samozřejmě použito ve více místech programu a změnit ho potřebujete jen v jednom místě. Přidání modifikačního dekorátoru v místě použití situaci neřeší. Jen systém znepřehledňuje.
No, abych byl uprimny tak se moc nechytam. c? Kdyz jsem delal tu chvilku v Haskellu, tak v tymu byla snaha mit funkce co nejkratsi a nejstrucnejsi, idealne vzdy s tim typovym zapisem (ten projektik presahl tisic radek urcite). Navic FP je snad zalozeno na tom, ze mame jasny vstup a vystup, vse bez vedlejsich efektu, jak se tedy c muze nekde ztracet?
-
tohle je samozřejmě validní námitka, můžu jenom dodat, že alfanumerické identifikátory neprovokují kreativitu tak jako přílišná volnost u operátorů, je to vidět na všelijakých EDSL (sám se taky občas neudržím)
No přiznejme si, že jmen za které bych autorovi otloukl hlavu o klávesnici, se vyskytuje taky dost :)
Ale je to pravda, svádí to. Možná i tím, že to ještě není tak prošláplé.
-
if a while jsou prvky imperativního programování. Je to stejný prohřešek, jako roubovat OOP do Lispu.
While se ve FP taky moc nevyskytuje, leda tak něco, co ho hodně vzdáleně připomíná. Funkcionální if (funkce podobná operátoru ?:) není nic roubovaného, ale naprosto plnohodnotný prvek FP.
Podstatou a výhodou funkcionálního programování by mělo být přiřazování (funkce = soubor uspořádaných entic), nikoliv představa, byť vzdálená, nějaké činnosti, výpočtu. if je je rozhodování, tedy výpočet, protože if je hodně obecné a vždy ho můžete nahradit konkrétní funkcí, pracující s přiřazením z konkrétně daných definičních oborů funkce. To je přece hlavní výhoda FP. Předem víte s čím pracujete. Je to dáno použitou funkcí. U obecného if to nevíte.
-
if a while jsou prvky imperativního programování. Je to stejný prohřešek, jako roubovat OOP do Lispu.
While se ve FP taky moc nevyskytuje, leda tak něco, co ho hodně vzdáleně připomíná. Funkcionální if (funkce podobná operátoru ?:) není nic roubovaného, ale naprosto plnohodnotný prvek FP.
Podstatou a výhodou funkcionálního programování by mělo být přiřazování (funkce = soubor uspořádaných entic), nikoliv představa, byť vzdálená, nějaké činnosti, výpočtu. if je je rozhodování, tedy výpočet, protože if je hodně obecné a vždy ho můžete nahradit konkrétní funkcí, pracující s přiřazením z konkrétně daných definičních oborů funkce. To je přece hlavní výhoda FP. Předem víte s čím pracujete. Je to dáno použitou funkcí. U obecného if to nevíte.
tak příklad:
poděl :: Integer -> Integer -> Maybe Integer
poděl a b = if b == 0 then Nothing else Just (a/b)
co je na tom nefunkcionálního?
-
if a while jsou prvky imperativního programování. Je to stejný prohřešek, jako roubovat OOP do Lispu.
While jo. Ale proč propánajána if? To jako "správně funkcionálně" mám každý if vyhodit ven jako samostatnou funkci? Proč, prokrýlepána?
-
Podstatou a výhodou funkcionálního programování by mělo být přiřazování (funkce = soubor uspořádaných entic), nikoliv představa, byť vzdálená, nějaké činnosti, výpočtu.
Takže a = b + c není FP? Vždyť je to výpočet.
if je je rozhodování, tedy výpočet, protože if je hodně obecné a vždy ho můžete nahradit konkrétní funkcí, pracující s přiřazením z konkrétně daných definičních oborů funkce. To je přece hlavní výhoda FP. Předem víte s čím pracujete. Je to dáno použitou funkcí. U obecného if to nevíte.
Mluvím o funkci if typu Bool -> a -> a -> a, která vrací buď druhý nebo třetí parametr. Co je na ní nefunkcionálního?
-
if a while jsou prvky imperativního programování. Je to stejný prohřešek, jako roubovat OOP do Lispu.
While se ve FP taky moc nevyskytuje, leda tak něco, co ho hodně vzdáleně připomíná. Funkcionální if (funkce podobná operátoru ?:) není nic roubovaného, ale naprosto plnohodnotný prvek FP.
Podstatou a výhodou funkcionálního programování by mělo být přiřazování (funkce = soubor uspořádaných entic), nikoliv představa, byť vzdálená, nějaké činnosti, výpočtu. if je je rozhodování, tedy výpočet, protože if je hodně obecné a vždy ho můžete nahradit konkrétní funkcí, pracující s přiřazením z konkrétně daných definičních oborů funkce. To je přece hlavní výhoda FP. Předem víte s čím pracujete. Je to dáno použitou funkcí. U obecného if to nevíte.
Nechápu. Nicméně každý if c then a else b můžete mechanicky nahradit funkcí λc.c a b, kde true je definováno jako λx y.x a false jako λx y.y
-
Složité myšlenky se špatně udržují, proto se do reálné praxe nehodí.
Takze misto napr. flatMap a foreach na jeden radek davate prednost ciste imperativnimu pristupu - ukecany cyklus na nekolik radku? Vzdyt to nic neprinasi, jen delsi kod. Nevim, prijde mi, ze slozite myslenky se lepe transformuji na jednoduche ve FP - mam nekolik funkci ktere postupne vstup zpracuji. V imperativnim programovani to casto skonci velkou metodou s nekolika vnorenymi cykly.
I reálné algoritmy v živé přírodě, které vznikly díky evoluci, nejsou ve své podstatě složité a jsou složeny z interakce jednoduchých elementů. A nejen tam. Například úplný soubor logických funkcí tvoří Shefferova funkce. No až v Haskellu nebude if a nebo while, tedy imperativní prvky, pak možná bude čitelný a elegantní :-) Zaměnit if a : y za y = if a : c moc elegantní není. Z hlediska čitelnosti programu je to katastrofa, protože se vám tam vynořuje to c, a musíte hledat, o co vlastně jde. Jistě u programu o 10 řádcích to problém není, problém to už je, když těch řádků jsou tisíce. Navíc, abyste někdy v budoucnosti mohl modifikovat to c, musíte dojít až k jeho zdroji, a zpětně prohledávat řetězy transformací imutable objektů, protože to c máte samozřejmě použito ve více místech programu a změnit ho potřebujete jen v jednom místě. Přidání modifikačního dekorátoru v místě použití situaci neřeší. Jen systém znepřehledňuje.
No, abych byl uprimny tak se moc nechytam. c? Kdyz jsem delal tu chvilku v Haskellu, tak v tymu byla snaha mit funkce co nejkratsi a nejstrucnejsi, idealne vzdy s tim typovym zapisem (ten projektik presahl tisic radek urcite). Navic FP je snad zalozeno na tom, ze mame jasny vstup a vystup, vse bez vedlejsich efektu, jak se tedy c muze nekde ztracet?
Ale přináší, přináší to možnost lepšího testování a snadnější lokalizaci chyby.
-
if a while jsou prvky imperativního programování. Je to stejný prohřešek, jako roubovat OOP do Lispu.
While se ve FP taky moc nevyskytuje, leda tak něco, co ho hodně vzdáleně připomíná. Funkcionální if (funkce podobná operátoru ?:) není nic roubovaného, ale naprosto plnohodnotný prvek FP.
Podstatou a výhodou funkcionálního programování by mělo být přiřazování (funkce = soubor uspořádaných entic), nikoliv představa, byť vzdálená, nějaké činnosti, výpočtu. if je je rozhodování, tedy výpočet, protože if je hodně obecné a vždy ho můžete nahradit konkrétní funkcí, pracující s přiřazením z konkrétně daných definičních oborů funkce. To je přece hlavní výhoda FP. Předem víte s čím pracujete. Je to dáno použitou funkcí. U obecného if to nevíte.
Nechápu. Nicméně každý if c then a else b můžete mechanicky nahradit funkcí λc.c a b, kde true je definováno jako λx y.x a false jako λx y.y
Voláním c a b, nikoliv funkcí.
-
if a while jsou prvky imperativního programování. Je to stejný prohřešek, jako roubovat OOP do Lispu.
While jo. Ale proč propánajána if? To jako "správně funkcionálně" mám každý if vyhodit ven jako samostatnou funkci? Proč, prokrýlepána?
Protože if je činnost, nikoliv funkce. Chápete-li ji funkcionálně, tedy jako přiřazení, je to přiřazení příliš široce definované. Předem nevíte s čím budete pracovat, její definice je příliš široká a univerzální. Tím se ztrácí výhoda FP programování.
-
Protože if je činnost, nikoliv funkce. Chápete-li ji funkcionálně, tedy jako přiřazení, je to přiřazení příliš široce definované. Předem nevíte s čím budete pracovat, její definice je příliš široká a univerzální. Tím se ztrácí výhoda FP programování.
To je teda pěkná blbost.
-
Podstatou a výhodou funkcionálního programování by mělo být přiřazování (funkce = soubor uspořádaných entic), nikoliv představa, byť vzdálená, nějaké činnosti, výpočtu.
Takže a = b + c není FP? Vždyť je to výpočet.
if je je rozhodování, tedy výpočet, protože if je hodně obecné a vždy ho můžete nahradit konkrétní funkcí, pracující s přiřazením z konkrétně daných definičních oborů funkce. To je přece hlavní výhoda FP. Předem víte s čím pracujete. Je to dáno použitou funkcí. U obecného if to nevíte.
Mluvím o funkci if typu Bool -> a -> a -> a, která vrací buď druhý nebo třetí parametr. Co je na ní nefunkcionálního?
c = a + b
Není to výpočet, je to výběr z množiny uspořádaných entic {..., [1,1,2], [1,2,3], ...}, myslíte-li funkcionálně a ne imperativně.
-
if a while jsou prvky imperativního programování. Je to stejný prohřešek, jako roubovat OOP do Lispu.
While jo. Ale proč propánajána if? To jako "správně funkcionálně" mám každý if vyhodit ven jako samostatnou funkci? Proč, prokrýlepána?
Už toho trolla, přátelé, fakt nekrmte.
-
Podstatou a výhodou funkcionálního programování by mělo být přiřazování (funkce = soubor uspořádaných entic), nikoliv představa, byť vzdálená, nějaké činnosti, výpočtu.
Takže a = b + c není FP? Vždyť je to výpočet.
if je je rozhodování, tedy výpočet, protože if je hodně obecné a vždy ho můžete nahradit konkrétní funkcí, pracující s přiřazením z konkrétně daných definičních oborů funkce. To je přece hlavní výhoda FP. Předem víte s čím pracujete. Je to dáno použitou funkcí. U obecného if to nevíte.
Mluvím o funkci if typu Bool -> a -> a -> a, která vrací buď druhý nebo třetí parametr. Co je na ní nefunkcionálního?
Není to výpočet, je to výběr z množiny uspořádaných entic {..., [1,1,2], [1,2,3], ...}, myslíte-li funkcionálně a ne imperativně.
To je IMO omezené chápání funkce na teorii množin - v programování se to moc nepoužívá.
-
if a while jsou prvky imperativního programování. Je to stejný prohřešek, jako roubovat OOP do Lispu.
While jo. Ale proč propánajána if? To jako "správně funkcionálně" mám každý if vyhodit ven jako samostatnou funkci? Proč, prokrýlepána?
Už toho trolla, přátelé, fakt nekrmte.
No už taky končím, ale člověka pobaví, když vidí, že zastánci FP myslí vlastně imperativně v FP jazyku :-)))
-
Už toho trolla, přátelé, fakt nekrmte.
+1
-
Podstatou a výhodou funkcionálního programování by mělo být přiřazování (funkce = soubor uspořádaných entic), nikoliv představa, byť vzdálená, nějaké činnosti, výpočtu.
Takže a = b + c není FP? Vždyť je to výpočet.
if je je rozhodování, tedy výpočet, protože if je hodně obecné a vždy ho můžete nahradit konkrétní funkcí, pracující s přiřazením z konkrétně daných definičních oborů funkce. To je přece hlavní výhoda FP. Předem víte s čím pracujete. Je to dáno použitou funkcí. U obecného if to nevíte.
Mluvím o funkci if typu Bool -> a -> a -> a, která vrací buď druhý nebo třetí parametr. Co je na ní nefunkcionálního?
Není to výpočet, je to výběr z množiny uspořádaných entic {..., [1,1,2], [1,2,3], ...}, myslíte-li funkcionálně a ne imperativně.
To je IMO omezené chápání funkce na teorii množin - v programování se to moc nepoužívá.
Tak ještě toto, v imperativním programování se toto nepoužívá, FP by se mělo, protože to pak vede na možnost strojově upravovat algoritmy napsané v FP jazyku tak, aby byly automaticky paralelizovatelné. A proto se vlastně FP zavádí, ne? Nebo jaké má vlastně výhody? Zvětšuje ego echt programátorů?
-
Protože if je činnost, nikoliv funkce. Chápete-li ji funkcionálně, tedy jako přiřazení, je to přiřazení příliš široce definované. Předem nevíte s čím budete pracovat, její definice je příliš široká a univerzální. Tím se ztrácí výhoda FP programování.
Wut? Neni to jen ekvivalent toho, ze muzu v Jave vracet vysledek typu Object? Snad jak moc obecny vysledek vratim je na programatorovi (a je uplne jedno, jestli to vratim z konstrukce if nebo funkce ci metody).
-
Protože if je činnost, nikoliv funkce. Chápete-li ji funkcionálně, tedy jako přiřazení, je to přiřazení příliš široce definované. Předem nevíte s čím budete pracovat, její definice je příliš široká a univerzální. Tím se ztrácí výhoda FP programování.
Wut? Neni to jen ekvivalent toho, ze muzu v Jave vracet vysledek typu Object? Snad jak moc obecny vysledek vratim je na programatorovi (a je uplne jedno, jestli to vratim z konstrukce if nebo funkce ci metody).
Ano, ale to je imperativní programování, díky neznámým závislostem mezi objekty nelze automatizovaně výpočet paralelizovat a dokazovat, což FP by mělo umožňovat. Ten if je samozřejmě extrém, ale na něm se ukáže, zda myslíte funkcionálně, či imperativně. Funkcionální programování vyžaduje oprostit se od pojmu čas, a tedy vás to osvobodí do jisté míry, od nutnosti zabývat se pořadím zpracování, výsledek matematické funkce závisí jen na vstupních hodnotách, nikoliv na pořadí zpracování funkcí ...
-
Podstatou a výhodou funkcionálního programování by mělo být přiřazování (funkce = soubor uspořádaných entic), nikoliv představa, byť vzdálená, nějaké činnosti, výpočtu.
Takže a = b + c není FP? Vždyť je to výpočet.
if je je rozhodování, tedy výpočet, protože if je hodně obecné a vždy ho můžete nahradit konkrétní funkcí, pracující s přiřazením z konkrétně daných definičních oborů funkce. To je přece hlavní výhoda FP. Předem víte s čím pracujete. Je to dáno použitou funkcí. U obecného if to nevíte.
Mluvím o funkci if typu Bool -> a -> a -> a, která vrací buď druhý nebo třetí parametr. Co je na ní nefunkcionálního?
Není to výpočet, je to výběr z množiny uspořádaných entic {..., [1,1,2], [1,2,3], ...}, myslíte-li funkcionálně a ne imperativně.
To je IMO omezené chápání funkce na teorii množin - v programování se to moc nepoužívá.
Tak ještě toto, v imperativním programování se toto nepoužívá, FP by se mělo, protože to pak vede na možnost strojově upravovat algoritmy napsané v FP jazyku tak, aby byly automaticky paralelizovatelné.
To jde i v imperativních jazycích, podívejte se třeba na jazyk ParaSail (https://forge.open-do.org/plugins/moinmoin/parasail/).
Jinak v mainstreamových funkcionálních jazycích je to s (explicitní) paralelizací dost slabé. OCaml má GIL, v Haskellu je těžké napsat paralelní algoritmus s dobrým výkonem (např. quicksort (http://flyingfrogblog.blogspot.cz/2010/08/parallel-generic-quicksort-in-haskell.html)), v F# a Scale máte prakticky totéž, co v C# a Javě.
-
if a while jsou prvky imperativního programování. Je to stejný prohřešek, jako roubovat OOP do Lispu.
While jo. Ale proč propánajána if? To jako "správně funkcionálně" mám každý if vyhodit ven jako samostatnou funkci? Proč, prokrýlepána?
Už toho trolla, přátelé, fakt nekrmte.
Ktereho? :D
-
Podstatou a výhodou funkcionálního programování by mělo být přiřazování (funkce = soubor uspořádaných entic), nikoliv představa, byť vzdálená, nějaké činnosti, výpočtu.
Takže a = b + c není FP? Vždyť je to výpočet.
if je je rozhodování, tedy výpočet, protože if je hodně obecné a vždy ho můžete nahradit konkrétní funkcí, pracující s přiřazením z konkrétně daných definičních oborů funkce. To je přece hlavní výhoda FP. Předem víte s čím pracujete. Je to dáno použitou funkcí. U obecného if to nevíte.
Mluvím o funkci if typu Bool -> a -> a -> a, která vrací buď druhý nebo třetí parametr. Co je na ní nefunkcionálního?
Není to výpočet, je to výběr z množiny uspořádaných entic {..., [1,1,2], [1,2,3], ...}, myslíte-li funkcionálně a ne imperativně.
To je IMO omezené chápání funkce na teorii množin - v programování se to moc nepoužívá.
Tak ještě toto, v imperativním programování se toto nepoužívá, FP by se mělo, protože to pak vede na možnost strojově upravovat algoritmy napsané v FP jazyku tak, aby byly automaticky paralelizovatelné.
To jde i v imperativních jazycích, podívejte se třeba na jazyk ParaSail (https://forge.open-do.org/plugins/moinmoin/parasail/).
Jinak v mainstreamových funkcionálních jazycích je to s (explicitní) paralelizací dost slabé. OCaml má GIL, v Haskellu je těžké napsat paralelní algoritmus s dobrým výkonem (např. quicksort (http://flyingfrogblog.blogspot.cz/2010/08/parallel-generic-quicksort-in-haskell.html)), v F# a Scale máte prakticky totéž, co v C# a Javě.
Dobře a jaké jsou tedy výhody funkcionálního přístupu?
-
Ano, ale to je imperativní programování, díky neznámým závislostem mezi objekty nelze automatizovaně výpočet paralelizovat a dokazovat, což FP by mělo umožňovat. Ten if je samozřejmě extrém, ale na něm se ukáže, zda myslíte funkcionálně, či imperativně. Funkcionální programování vyžaduje oprostit se od pojmu čas, a tedy vás to osvobodí do jisté míry, od nutnosti zabývat se pořadím zpracování, výsledek matematické funkce závisí jen na vstupních hodnotách, nikoliv na pořadí zpracování funkcí ...
A to by mě teda docela zajímalo.
První námitka: Podle mě můžu definovat trojici {a,b,c}, která odpovídá ifu. Takže máme ten kýžený výběr z trojic. Pokud to tak nejde, chtěl bych vidět nějakou pořádnou argumentaci.
Co jsou "neznámé závislosti", nevím. Pořadí vyhodnocení můžu u ifu měnit jako u jakékoliv jiné fce.
-
Ano, ale to je imperativní programování, díky neznámým závislostem mezi objekty nelze automatizovaně výpočet paralelizovat a dokazovat, což FP by mělo umožňovat. Ten if je samozřejmě extrém, ale na něm se ukáže, zda myslíte funkcionálně, či imperativně. Funkcionální programování vyžaduje oprostit se od pojmu čas, a tedy vás to osvobodí do jisté míry, od nutnosti zabývat se pořadím zpracování, výsledek matematické funkce závisí jen na vstupních hodnotách, nikoliv na pořadí zpracování funkcí ...
A to by mě teda docela zajímalo.
První námitka: Podle mě můžu definovat trojici {a,b,c}, která odpovídá ifu. Takže máme ten kýžený výběr z trojic. Pokud to tak nejde, chtěl bych vidět nějakou pořádnou argumentaci.
Co jsou "neznámé závislosti", nevím. Pořadí vyhodnocení můžu u ifu měnit jako u jakékoliv jiné fce.
No ten if byl extrém. Jak si ho vizualizujete, když programujete? Jako porovnání konkrétních hodnot, předpokládám, tedy imperativním způsobem myšlení.
-
Ano, ale to je imperativní programování, díky neznámým závislostem mezi objekty nelze automatizovaně výpočet paralelizovat a dokazovat, což FP by mělo umožňovat. Ten if je samozřejmě extrém, ale na něm se ukáže, zda myslíte funkcionálně, či imperativně. Funkcionální programování vyžaduje oprostit se od pojmu čas, a tedy vás to osvobodí do jisté míry, od nutnosti zabývat se pořadím zpracování, výsledek matematické funkce závisí jen na vstupních hodnotách, nikoliv na pořadí zpracování funkcí ...
A to by mě teda docela zajímalo.
První námitka: Podle mě můžu definovat trojici {a,b,c}, která odpovídá ifu. Takže máme ten kýžený výběr z trojic. Pokud to tak nejde, chtěl bych vidět nějakou pořádnou argumentaci.
Co jsou "neznámé závislosti", nevím. Pořadí vyhodnocení můžu u ifu měnit jako u jakékoliv jiné fce.
Pořadí vyhodnocení u if? No například if (f1() && f2()) v imperativních jazycích znamená, že když f2 má vedlejší efekt, tak dojde k chybě, protože když f1() vrací false, tak f2() se nevolá. A to jsou ty neznámé závislosti.
-
No ten if byl extrém. Jak si ho vizualizujete, když programujete? Jako porovnání konkrétních hodnot, předpokládám, tedy imperativním způsobem myšlení.
To je úplně irelevantní, jak si ho vyzualizuju. Řekněme tedy jako růžového slona. Spíš bych chtěl teda slyšet, proč to nemůže být ta relace.
-
Pořadí vyhodnocení u if? No například if (f1() && f2()) v imperativních jazycích znamená, že když f2 má vedlejší efekt, tak dojde k chybě, protože když f1() vrací false, tak f2() se nevolá. A to jsou ty neznámé závislosti.
Nebavíme se o imperativních jazycích. Bavíme se o FP bez vedlejších efektů. Proč tam teda if nepatří?
-
Pořadí vyhodnocení u if? No například if (f1() && f2()) v imperativních jazycích znamená, že když f2 má vedlejší efekt, tak dojde k chybě, protože když f1() vrací false, tak f2() se nevolá. A to jsou ty neznámé závislosti.
Tohle ale není o pořadí vyhodnocení u ifu ale u &&. To samé by se dělo, pokud bych místo ifu výsledek && použil jakkoliv jinak.
-
A FP jazyky ji mají přímo katastrofální.
Tak to určitě...
-
Ano, ale to je imperativní programování, díky neznámým závislostem mezi objekty nelze automatizovaně výpočet paralelizovat a dokazovat, což FP by mělo umožňovat. Ten if je samozřejmě extrém, ale na něm se ukáže, zda myslíte funkcionálně, či imperativně. Funkcionální programování vyžaduje oprostit se od pojmu čas, a tedy vás to osvobodí do jisté míry, od nutnosti zabývat se pořadím zpracování, výsledek matematické funkce závisí jen na vstupních hodnotách, nikoliv na pořadí zpracování funkcí ...
A to by mě teda docela zajímalo.
První námitka: Podle mě můžu definovat trojici {a,b,c}, která odpovídá ifu. Takže máme ten kýžený výběr z trojic. Pokud to tak nejde, chtěl bych vidět nějakou pořádnou argumentaci.
Co jsou "neznámé závislosti", nevím. Pořadí vyhodnocení můžu u ifu měnit jako u jakékoliv jiné fce.
No ten if byl extrém. Jak si ho vizualizujete, když programujete? Jako porovnání konkrétních hodnot, předpokládám, tedy imperativním způsobem myšlení.
If je normální matematická funkce, nejjednodušším příkladem je třeba |x|.
-
If je normální matematická funkce, nejjednodušším příkladem je třeba |x|.
Taky jsem na to koukal. Vzdyt jedna z moznosti jak formale definovat funkci je tabulkou, kde if lze jednoduse zapsat.
-
If je normální matematická funkce, nejjednodušším příkladem je třeba |x|.
Taky jsem na to koukal. Vzdyt jedna z moznosti jak formale definovat funkci je tabulkou, kde if lze jednoduse zapsat.
Je to troll, fakt.
-
Je to troll, fakt.
Když se někdo splete, tak nemusí být hnedka troll. Spíš mě udivuje, s jakou vehemencí tady lidí tvrdí blbosti a neposlouchají námitky.
-
Je to troll, fakt.
Když se někdo splete, tak nemusí být hnedka troll. Spíš mě udivuje, s jakou vehemencí tady lidí tvrdí blbosti a neposlouchají námitky.
Jenže já ho pozoruju už delší dobu.
-
https://developer.apple.com/videos/wwdc/2015/?id=408
-
https://developer.apple.com/videos/wwdc/2015/?id=408
(http://i.imgur.com/cJqJKrk.png)
^ FF i Chrome. Vidim, ze v ovocne firme opravdu jedou na UX.
-
https://developer.apple.com/videos/wwdc/2015/?id=408
(http://i.imgur.com/cJqJKrk.png)
^ FF i Chrome. Vidim, ze v ovocne firme opravdu jedou na UX.
Stačí to PDF
-
Je to troll, fakt.
Když se někdo splete, tak nemusí být hnedka troll. Spíš mě udivuje, s jakou vehemencí tady lidí tvrdí blbosti a neposlouchají námitky.
Jenže já ho pozoruju už delší dobu.
Pokud je to troll, tak proč ho pořád krmíš? Už mě lehce iritují tvé trollující kecy o trollení.
-
https://developer.apple.com/videos/wwdc/2015/?id=408
Nejsou virtuální metody, není dědičnost, jsou jenom interface pod novým názvem protokol. Objektivně je to tedy méně hodnotné než OOP a na funkcionalitu virtuálních metod, tedy starý kód může volat nový, se vykašlali úplně.
-
https://developer.apple.com/videos/wwdc/2015/?id=408
vypadá to jako Type Classes z Haskellu (které jsou prý nic proti Modules z OCamlu) nebo se mi to zdá?
-
https://developer.apple.com/videos/wwdc/2015/?id=408
Nejsou virtuální metody, není dědičnost, jsou jenom interface pod novým názvem protokol. Objektivně je to tedy méně hodnotné než OOP a na funkcionalitu virtuálních metod, tedy starý kód může volat nový, se vykašlali úplně.
Protokol je původní název pro tento koncept. Dědičnost protokolů tam je. Virtuální metody ("defaultní") tam jsou taky. Někdo má problémy s porozuměním jednoduchému textu...
-
https://developer.apple.com/videos/wwdc/2015/?id=408
vypadá to jako Type Classes z Haskellu (které jsou prý nic proti Modules z OCamlu) nebo se mi to zdá?
Nezdá
-
Nejsou virtuální metody, není dědičnost, jsou jenom interface pod novým názvem protokol. Objektivně je to tedy méně hodnotné než OOP a na funkcionalitu virtuálních metod, tedy starý kód může volat nový, se vykašlali úplně.
Objektivně? :D Jaká jsou prosím objektivní kritéria pro tu hodnotnost?
Ten interface přece obsahuje virtuální metody a dědičnost těch interfaců tam je. Takže jediné, co chybí je dědičnost plných tříd. A ta se přes dědičnost interfaců a kompozici dá udělat s prstem v nose. Na to, aby starý kód mohl volat ten nový, ty interfacy bohatě stačí.
-
https://developer.apple.com/videos/wwdc/2015/?id=408
vypadá to jako Type Classes z Haskellu (které jsou prý nic proti Modules z OCamlu) nebo se mi to zdá?
-
Pokud je to troll, tak proč ho pořád krmíš? Už mě lehce iritují tvé trollující kecy o trollení.
Pokud Ti vadí celé moje DVA příspěvky o trollení, máš to fakt hloupé.
-
Pokud je to troll, tak proč ho pořád krmíš? Už mě lehce iritují tvé trollující kecy o trollení.
Pokud Ti vadí celé moje DVA příspěvky o trollení, máš to fakt hloupé.
dva by se daly tolerovat, ale tohle je třetí a to už je fakt moc
-
Nejsou virtuální metody, není dědičnost, jsou jenom interface pod novým názvem protokol.
Pojem protokol se Objective-C pouzival, kdyz jsme jeste vsichni tahali kacera :)
-
Protokol je původní název pro tento koncept. Dědičnost protokolů tam je. Virtuální metody ("defaultní") tam jsou taky. Někdo má problémy s porozuměním jednoduchému textu...
Z PDF se zdá jako že by protokoly snad měly být lepší náhrada za běžné OOP a to není pravda.
Vím že ve Swiftu jsou i class a ty fungují "postaru". Stejně tak "postaru" ve Swiftu dělají přepsání virtuální metody v potomku.
-
https://developer.apple.com/videos/wwdc/2015/?id=408
vypadá to jako Type Classes z Haskellu (které jsou prý nic proti Modules z OCamlu) nebo se mi to zdá?
Nezdá
Mě jako haskellistu by moc zajímali podrobnosti. Nemáš nějaký pěkný pokec o tom? Nebo to tu alespoň trochu rozveď.
-
Aby tu nebylo jen pusté filosofování, zde je "protocol-oriented" kód (ať to není příliš dlouhé, tak jen pro nejobecnější a nejspeciálnější případ):
protocol QuadrilateralWithParallelOppositeSides {
var side1:Double { get }
var side2:Double { get }
var skew:Double { get }
}
extension QuadrilateralWithParallelOppositeSides {
func area() -> Double {
return side1 * side2 * cos(skew)
}
}
struct Parallelogram : QuadrilateralWithParallelOppositeSides {
var side1:Double
var side2:Double
var skew:Double
init(side1 s1:Double, side2 s2:Double, skew s:Double) {
side1 = s1
side2 = s2
skew = s
}
}
struct Square : QuadrilateralWithParallelOppositeSides {
var side1:Double
var side2:Double { return side1 }
var skew:Double { return 0 }
init(side s1:Double) {
side1 = s1
}
}
let figure1 = Parallelogram(side1: 2, side2: 3, skew: M_PI_4)
let figure2 = Square(side: 3)
var list:Array<QuadrilateralWithParallelOppositeSides> = [ figure1, figure2 ]
print(list.map { $0.area() })
Jak je hezky vidět, metoda pro výpočet obsahu se definuje jen jednou. O různé invarianty se postarají "computed properties". Jednoduché, elegantní a rozšiřitelné :)
Doplnění k tomuto evidentně nejlepšímu řešení: Ve Swiftu je pro čtverec ona generická metoda stejně rychlá jako side1*side1 (agresivní optimalizace). V C++ clang generickou metodu také optimalizuje, ale zrychlení není o řád. Microsoftí C++ neoptimalizuje nic. C# zrychlí defaultní metodu asi o polovinu a Java (osmička) 4-5x. Sečteno a podtrženo: Swift jasně vede (v implementaci), ale kromě Microsoftího C++ si mainstream s optimalizací generické metody pro výpočet obsahu rovnoběžníku hravě poradí.
-
Mě jako haskellistu by moc zajímali podrobnosti. Nemáš nějaký pěkný pokec o tom? Nebo to tu alespoň trochu rozveď.
Taky by me to zajimalo.. zkousel jsem to vygooglit ale nic jsem nenasel.
-
Mě jako haskellistu by moc zajímali podrobnosti. Nemáš nějaký pěkný pokec o tom? Nebo to tu alespoň trochu rozveď.
Taky by me to zajimalo.. zkousel jsem to vygooglit ale nic jsem nenasel.
Já Haskell moc neznám. Zde je explicitně zmíněn:
https://en.m.wikipedia.org/wiki/Protocol_(object-oriented_programming)
https://mobile.twitter.com/bos31337/status/473545340864700416
https://bigonotetaking.wordpress.com/2015/07/17/swift-protocols-a-strategy/
-
Protokol je původní název pro tento koncept. Dědičnost protokolů tam je. Virtuální metody ("defaultní") tam jsou taky. Někdo má problémy s porozuměním jednoduchému textu...
Z PDF se zdá jako že by protokoly snad měly být lepší náhrada za běžné OOP a to není pravda.
Vím že ve Swiftu jsou i class a ty fungují "postaru". Stejně tak "postaru" ve Swiftu dělají přepsání virtuální metody v potomku.
Specializace protokolu je většinou lepší než odvození z nadtřídy. Viz příklad někde výše, kde čtverec dědí z rovnoběžníku. V "běžném" OOP nejde v žádném jazyce mít společnou metodu pro výpočet obsahu a přitom méně atributů u čtverce. Principiálně tomu sice nic nebrání, ale v C++, C#, ObjC ani Javě to prostě nejde.
-
Z PDF se zdá jako že by protokoly snad měly být lepší náhrada za běžné OOP a to není pravda.
Je to pravda. Častý problém programátorů-praktiků je, že jako "lepší" si představují to, co má víc čudlíků, hejblátek, generuje to víc kódu apod. Viz např. "můj jazyk je lepší, protože se v něm dá dělat i x" (hurá, "multiparadigmatický jazyk"). Ve skutečnosti je to ale spíš opačně - čím je věc omezenější, tím tam platí silnější předpoklady a tím se mj. dá i líp optimalizovat. A často je taky v důsledku obecnější, protože stačí splnit pevné předpoklady a platí spousta důsledků.
Protokoly jsou k tomu, aby se ke všem věcem, které splňují nějaké předpoklady, dalo chovat stejným způsobem. Podobně jako v OOP když mají společného předka. Akorát u protokolů nemusíš třešit tragikomické důsledky hierarchizace dědičnosti. Prostě to kváká jako kachna, tak je to kachna. Co víc chtít?!
-
Z PDF se zdá jako že by protokoly snad měly být lepší náhrada za běžné OOP a to není pravda.
Je to pravda. Častý problém programátorů-praktiků je, že jako "lepší" si představují to, co má víc čudlíků, hejblátek, generuje to víc kódu apod. Viz např. "můj jazyk je lepší, protože se v něm dá dělat i x" (hurá, "multiparadigmatický jazyk"). Ve skutečnosti je to ale spíš opačně - čím je věc omezenější, tím tam platí silnější předpoklady a tím se mj. dá i líp optimalizovat. A často je taky v důsledku obecnější, protože stačí splnit pevné předpoklady a platí spousta důsledků.
Protokoly jsou k tomu, aby se ke všem věcem, které splňují nějaké předpoklady, dalo chovat stejným způsobem. Podobně jako v OOP když mají společného předka. Akorát u protokolů nemusíš třešit tragikomické důsledky hierarchizace dědičnosti. Prostě to kváká jako kachna, tak je to kachna. Co víc chtít?!
Aneb Einsteinovo "Everything Should Be Made as Simple as Possible, But Not Simpler".
P.S. s/praktik/lepič
-
P.S. s/praktik/lepič
Nemusí to být lepič. Stačí aby se s něčím setkal poprvé. Ona ta elegance na první pohled vidět není. Je třeba začít o problému uvažovat trochu jinak.
-
P.S. s/praktik/lepič
Nemusí to být lepič. Stačí aby se s něčím setkal poprvé. Ona ta elegance na první pohled vidět není. Je třeba začít o problému uvažovat trochu jinak.
Jo, to je pravda. Mně jen vadí, když někdo kritizuje něco, než se s tím seznámí.
-
https://developer.apple.com/videos/wwdc/2015/?id=408
Když uz jsme u toho, nevíte někdo, proč v C# udělali tak debilně extension methods v cizích statických třídách? Java to má aspoň pohromadě.
-
https://developer.apple.com/videos/wwdc/2015/?id=408
Když uz jsme u toho, nevíte někdo, proč v C# udělali tak debilně extension methods v cizích statických třídách?
A v čem je to debilní, resp. jak by to mělo být?
-
https://developer.apple.com/videos/wwdc/2015/?id=408
Když uz jsme u toho, nevíte někdo, proč v C# udělali tak debilně extension methods v cizích statických třídách?
A v čem je to debilní, resp. jak by to mělo být?
Má to být přímo v tom rozhraní, jako v Javě.
-
Tady ale směšuješ dvě různé věci. Defaultní metody v Javě slouží jednak k omezené vícenásobné dědičnosti a jednak k tomu, aby bylo možné bezpečně rozšiřovat interfacy, které jsou součástí veřejných API. Druhý bod byl Oraclem prezentován jako jejich hlavní selling point, tzn. že defaultní metody jim umožnili rozšířit do té doby zamrzlé API kolekcí. Sranda je to, že nakonec k tomu prakticky vůbec nedošlo a veškeré funkcionální operace se místo kolekcí dostaly do nového typu - streamů.
Extension metody v C# naproti tomu slouží k rozšiřování tříd, nad kterými nemám kontrolu (jsou součástí SDK, knihoven atd.) a to v Javě dodnes není možné. Proto např. Guava definuje spoustu statických tříd pojmenovaných plurálem třídy, nad kterou operují (Strings, Ints, Iterables, Lists atd.). Takže pak můžu dělat toto: Lists.filter(list, predicate), ale už ne tohle: list.filter(predicate).
-
https://developer.apple.com/videos/wwdc/2015/?id=408
Když uz jsme u toho, nevíte někdo, proč v C# udělali tak debilně extension methods v cizích statických třídách?
A v čem je to debilní, resp. jak by to mělo být?
Má to být přímo v tom rozhraní, jako v Javě.
Java nemá extension metody vůbec, ta má jen defaultní metody, ne? Navíc u defaultních metod musíte mít možnost rozhraní měnit (tj. přístup k jeho kódu).
Extension metody v C# jsou jen syntaktický cukr - kdyby mělo jít rozšířit existující rozhraní (bez možnosti ho přímo měnit - např. rozhraní z jiné assembly) nebo implementovat rozhraní pro existující třídu (bez možnosti ji přímo měnit), vyžadovalo by to změny v CLR (případně obejít CLR).
Výhodou stávajícího řešení je, že můžete mít více implementací jedné extension metody a není třeba měnit CLR.
BTW může třída ve Swiftu mít více implementací jednoho protokolu?
-
Prostě to kváká jako kachna, tak je to kachna. Co víc chtít?!
Todle mi třeba vadí:
interface Foo
{
string quak();
}
interface Doo
{
string quak();
}
class DuckFoo implements Foo { .. }
class DuckDoo implements Doo { .. }
print(? duck) {
std.out.print(duck.quak())
}
print(new DuckFoo)
print(new DuckDoo)
-
Tady ale směšuješ dvě různé věci. Defaultní metody v Javě slouží jednak k omezené vícenásobné dědičnosti a jednak k tomu, aby bylo možné bezpečně rozšiřovat interfacy, které jsou součástí veřejných API. Druhý bod byl Oraclem prezentován jako jejich hlavní selling point, tzn. že defaultní metody jim umožnili rozšířit do té doby zamrzlé API kolekcí. Sranda je to, že nakonec k tomu prakticky vůbec nedošlo a veškeré funkcionální operace se místo kolekcí dostaly do nového typu - streamů.
Extension metody v C# naproti tomu slouží k rozšiřování tříd, nad kterými nemám kontrolu (jsou součástí SDK, knihoven atd.) a to v Javě dodnes není možné. Proto např. Guava definuje spoustu statických tříd pojmenovaných plurálem třídy, nad kterou operují (Strings, Ints, Iterables, Lists atd.). Takže pak můžu dělat toto: Lists.filter(list, predicate), ale už ne tohle: list.filter(predicate).
Jenže v C# to jde i pro rozhraní.
-
https://developer.apple.com/videos/wwdc/2015/?id=408
Když uz jsme u toho, nevíte někdo, proč v C# udělali tak debilně extension methods v cizích statických třídách?
A v čem je to debilní, resp. jak by to mělo být?
Má to být přímo v tom rozhraní, jako v Javě.
Java nemá extension metody vůbec, ta má jen defaultní metody, ne? Navíc u defaultních metod musíte mít možnost rozhraní měnit (tj. přístup k jeho kódu).
Extension metody v C# jsou jen syntaktický cukr - kdyby mělo jít rozšířit existující rozhraní (bez možnosti ho přímo měnit - např. rozhraní z jiné assembly) nebo implementovat rozhraní pro existující třídu (bez možnosti ji přímo měnit), vyžadovalo by to změny v CLR (případně obejít CLR).
Výhodou stávajícího řešení je, že můžete mít více implementací jedné extension metody a není třeba měnit CLR.
BTW může třída ve Swiftu mít více implementací jednoho protokolu?
Může mít více implementací jedné metody.
-
Tady ale směšuješ dvě různé věci. Defaultní metody v Javě slouží jednak k omezené vícenásobné dědičnosti a jednak k tomu, aby bylo možné bezpečně rozšiřovat interfacy, které jsou součástí veřejných API. Druhý bod byl Oraclem prezentován jako jejich hlavní selling point, tzn. že defaultní metody jim umožnili rozšířit do té doby zamrzlé API kolekcí. Sranda je to, že nakonec k tomu prakticky vůbec nedošlo a veškeré funkcionální operace se místo kolekcí dostaly do nového typu - streamů.
Extension metody v C# naproti tomu slouží k rozšiřování tříd, nad kterými nemám kontrolu (jsou součástí SDK, knihoven atd.) a to v Javě dodnes není možné. Proto např. Guava definuje spoustu statických tříd pojmenovaných plurálem třídy, nad kterou operují (Strings, Ints, Iterables, Lists atd.). Takže pak můžu dělat toto: Lists.filter(list, predicate), ale už ne tohle: list.filter(predicate).
Jenže v C# to jde i pro rozhraní.
Teď si asi nerozumíme. Jestli jde o třídu/rozhraní/trait/whatever je jedno. Rozdíl je v tom, že zatímco v Javě mohu defaultní metody umisťovat pouze do svého vlastního kódu, tak v C# lze extension metodami rozšiřovat i typy, které nevlastním.
Jinak řečeno, extension metody jsou nástroj jak pro autora API, tak pro jeho klienta, default metody naproti tomu pouze pro autora.
-
Tady ale směšuješ dvě různé věci. Defaultní metody v Javě slouží jednak k omezené vícenásobné dědičnosti a jednak k tomu, aby bylo možné bezpečně rozšiřovat interfacy, které jsou součástí veřejných API. Druhý bod byl Oraclem prezentován jako jejich hlavní selling point, tzn. že defaultní metody jim umožnili rozšířit do té doby zamrzlé API kolekcí. Sranda je to, že nakonec k tomu prakticky vůbec nedošlo a veškeré funkcionální operace se místo kolekcí dostaly do nového typu - streamů.
Extension metody v C# naproti tomu slouží k rozšiřování tříd, nad kterými nemám kontrolu (jsou součástí SDK, knihoven atd.) a to v Javě dodnes není možné. Proto např. Guava definuje spoustu statických tříd pojmenovaných plurálem třídy, nad kterou operují (Strings, Ints, Iterables, Lists atd.). Takže pak můžu dělat toto: Lists.filter(list, predicate), ale už ne tohle: list.filter(predicate).
Jenže v C# to jde i pro rozhraní.
Teď si asi nerozumíme. Jestli jde o třídu/rozhraní/trait/whatever je jedno. Rozdíl je v tom, že zatímco v Javě mohu defaultní metody umisťovat pouze do svého vlastního kódu, tak v C# lze extension metodami rozšiřovat i typy, které nevlastním.
Jinak řečeno, extension metody jsou nástroj jak pro autora API, tak pro jeho klienta, default metody naproti tomu pouze pro autora.
Jasně, mně se jen nelíbí, jak se to zapisuje. Ve Swiftu prostě napíšu "extension String ..." a v ObjC "@interface NSString ()" a můžu rozšiřovat. V C# by to mělo být něco jako "partial class ..." (a místo partial třeba extension). Je to jen syntax, ale teď je to nelogické.