Omezená dědičnost (je něco lepšího než OOP?)

JSH

Re:Omezená dědičnost (je něco lepšího než OOP?)
« Odpověď #165 kdy: 11. 09. 2015, 16:48:45 »
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.


zboj

  • *****
  • 1 507
    • Zobrazit profil
    • E-mail
Re:Omezená dědičnost (je něco lepšího než OOP?)
« Odpověď #166 kdy: 11. 09. 2015, 16:52:43 »
Poněkud názornější (a užitečnější) příklad protocol-based programming: 

Kód: [Vybrat]
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)")

JSH

Re:Omezená dědičnost (je něco lepšího než OOP?)
« Odpověď #167 kdy: 11. 09. 2015, 16:58:27 »
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?

gamer

Re:Omezená dědičnost (je něco lepšího než OOP?)
« Odpověď #168 kdy: 11. 09. 2015, 18:32:17 »
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.

Re:Omezená dědičnost (je něco lepšího než OOP?)
« Odpověď #169 kdy: 11. 09. 2015, 18:45:06 »
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í.


Re:Omezená dědičnost (je něco lepšího než OOP?)
« Odpověď #170 kdy: 11. 09. 2015, 18:48:03 »
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.

Re:Omezená dědičnost (je něco lepšího než OOP?)
« Odpověď #171 kdy: 11. 09. 2015, 18:50:59 »
tak to je klasická SIMD situace a používá se na to map+reduce.
Sorry, překlep, mělo být "MISD".

gamer

Re:Omezená dědičnost (je něco lepšího než OOP?)
« Odpověď #172 kdy: 11. 09. 2015, 18:58:30 »
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.

Re:Omezená dědičnost (je něco lepšího než OOP?)
« Odpověď #173 kdy: 11. 09. 2015, 19:04:51 »
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).

JS

Re:Omezená dědičnost (je něco lepšího než OOP?)
« Odpověď #174 kdy: 11. 09. 2015, 19:09:27 »
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.

JS

Re:Omezená dědičnost (je něco lepšího než OOP?)
« Odpověď #175 kdy: 11. 09. 2015, 19:14:23 »
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.

gamer

Re:Omezená dědičnost (je něco lepšího než OOP?)
« Odpověď #176 kdy: 11. 09. 2015, 19:15:32 »
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ě.

Re:Omezená dědičnost (je něco lepšího než OOP?)
« Odpověď #177 kdy: 11. 09. 2015, 19:23:47 »
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.

gamer

Re:Omezená dědičnost (je něco lepšího než OOP?)
« Odpověď #178 kdy: 11. 09. 2015, 19:26:28 »
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í.

Re:Omezená dědičnost (je něco lepšího než OOP?)
« Odpověď #179 kdy: 11. 09. 2015, 19:41:35 »
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ý :)