Díky moc za další zajímavý díl!
Věci popsané teď se mi (na první pohled) ale už moc nelíbí, přijdou mi takové podezřele nefunkcionální a trochu narušující koncept :) Mám pár otázek (je ranní hodina, takže pokud některé budou mimo mísu, prosím o shovívavost :)
1. K čemu přesně se hodí promise, co by nešlo udělat s future?
Napadá mě třeba použít promise jako semafor: spustím deset vláken s nějakým výpočtem a když jedno výpočet zvládne rychleji, nastaví "proměnnou" (ale fuj! :) a ostatní vlákna už se s výpočtem nemusí párat.
Je to tak, nebo to má ještě nějaké jiné využití?
2. Jak u promise/future fungují výjimky? Když při výpočtu dojde k výjimce, vyhodí se až tehdy, když se pokusím přečíst hodnotu?
3. Co si mám představit pod tím "kopie všech referencí"? Co když chci pracovat s javovským objektem? Provede se (jak?!) hluboká kopie, nebo jenom mělká (takže vlastně transakce s javovskými objekty vůbec nemůžu použít)? Nebo jde o něco úplně jiného?
> Systém v tomto případě jednoduše transakci spustí znova a využije přitom již změněné hodnoty.
Znamená to, že s trochou (ne)štěstí se může transakce opakovat donekonečna? To zavání možným deadlockem...
> Při práci s transakcemi je nutné dbát především na to, aby se do funkce alter předávaly jen takové funkce, které nemají vedlejší efekt.
To je docela blbý. Už je to druhý místo (po lazy sekvencích), kde si na funkce s vedlejším efektem musím dávat pozor. Přijde mi to hodně napraktický a ve skutečné aplikaci si to moc neumím představit - volání se můžou zřetězovat do velké hloubky a zjistit, jestli tam někde je nebo není vedlejší efekt, může být docela problém, ne? Kdybych to chtěl udělat úplně čistě, musel bych si všechny funkce s vedlejším efektem nějak označit s tím, že tenhle flag by se pak šířil do všech funkcí, které je budou volat...
=====
Celkově mi to na první pohled přijde trochu ne-čistý. Pokud u těch transakcí tak jako tak musím dávat pozor na vedlejší efekty, proč nepoužít rovnou úplně stupidní čistě funkcionální přístup, který mi defacto (pokud nemám vedlejší efekty) ACID zaručuje taky?
Erlang:
transfer_money(Balance1,Balance2,Amount) -> {Balance1-Amount,Balance2+Amount}.
Je to tím, že se potenciálně operuje nad javovskými objekty a proto musím mít explicitní transakce? Nebo jaký je důvod funkcionální jazyk komplikovat takovýmihle věcmi?
Předem díky za reakci. Znovu opakuju: pokud jsou otázky mimo mísu, prosím o shovívavost :)
3. Co si mám představit pod tím "kopie všech referencí"? Co když chci pracovat s javovským objektem? Provede se (jak?!) hluboká kopie, nebo jenom mělká (takže vlastně transakce s javovskými objekty vůbec nemůžu použít)?
IMO slovem "referencí" se myslí objektů typu ref. STM hlídá pouze objekty typu ref a změna jiných objektů nebo zápisy do souborů jsou právě ty zlé vedlejší efekty, jenž by se neměly používat v transakcích.
Kdybych to chtěl udělat úplně čistě, musel bych si všechny funkce s vedlejším efektem nějak označit s tím, že tenhle flag by se pak šířil do všech funkcí, které je budou volat...
Například v Haskellu to typový systém udělá za Vás.
Celkově mi to na první pohled přijde trochu ne-čistý. Pokud u těch transakcí tak jako tak musím dávat pozor na vedlejší efekty, proč nepoužít rovnou úplně stupidní čistě funkcionální přístup, který mi defacto (pokud nemám vedlejší efekty) ACID zaručuje taky?
Tady jde o sdílení měnitelného stavu více vlákny/procesy.
Zdravim,
temi referencemi jsem myslel ref(s) ve smyslu, jak je pouziva Clojure.
Jinak ciste teoreticky se muze stat, ze jedna transakce se bude porad opakovat, stejne jako se muze stat, ze nekde v Jave zustane jedno "vyhladovele" vlakno kvuli spatnemu scheduleru. Prakticky se Clojure snazi uprednostit commity pred vytvarenim novych transakci, takze by se to melo omezit, ale priznam se, ze jestli je tam nejaky mechanismus na detekci starvation, nevim.
Clojure skutecne umi pracovat s Javovskymi objekty, coz neni ciste, uz jen kvuli tomu, ze se typicky jedna o mutable objekty. Prave priste se budu zabyvat tim, jak se (spatne) pracuje s arrays z Javy a jake jsou tam rozdily oproti vektorum. Ale jestli ma Clojure fungovat nad JVM i s jejimi knihovnami, tak se tomu nevyhneme - a je skoda, ze spousta objektu v Java API, ktere si primo rikaji o immutabilitu, ma nejake settery :/
"Přijde mi to hodně napraktický a ve skutečné aplikaci si to moc neumím představit - volání se můžou zřetězovat do velké hloubky a zjistit, jestli tam někde je nebo není vedlejší efekt, může být docela problém, ne?"
Pokud nevíš, zda funkce má nějaký vedlejší efekt, tak to značí, že programuješ jak čuně, a problém s údržbou takového programu budeš mít v jakémkoli jazyce.
Ano, transakce a napriklad i pouziti future ci agentu (priste) ma sva omezeni, ale zalezi skutecne na programatorovi, jestli tato omezeni akceptuje a dokaze tyto technologie vyuzit.
V kazdem pripade se muze kdykoli vratit na uroven locku (nebo synchronizovanych metod/bloku v Jave, coz je v podstate to stejne) a resit si to sam - ovsem s tim, ze je opet JEN NA NEM, jestli tam udela/neudela potencialni deadlock. Stejne jako pri pouzivani treba nesynchronizovanych kolekci atd.
Bylo by fajn mit nejakou metodu pro uplne automatickou paralelizaci, neco jako dataflow, ale u funkci s vedlejsim efektem (nebo rekneme pri volani setteru) to bude AFAIK problem vzdycky.
jeste mozna doplnim, ze treba podporu pro paralelizaci funkci typu map/reduce/apply mel parallel lisp, jenze tento pristup asi nepujde vyuzit vzdycky. Mam na mysli to, ze je sice fajn umet paralelne zpracovat seznam ci strom, ale v praxi jsou ty systemy slozitejsi a spis je nutne resit synchronizaci vice klientu atd. atd.
Nebo to vsechno serializovat pres locky a udelat si tak vlastni registr vozidel, kterej nezvladne ani pitomych zhruba 18000 zaznamu za den :-)))