2146
Vývoj / Re:Ideálny programovací jazyk
« kdy: 15. 05. 2019, 23:49:15 »Určitě?Filip má velmi efektivní selektivní vnímáníNikoli, já pouze vím – na rozdíl od vás – jak to funguje.
Tato sekce Vám umožňuje zobrazit všechny příspěvky tohoto uživatele. Prosím uvědomte si, že můžete vidět příspěvky pouze z oblastí Vám přístupných.
Určitě?Filip má velmi efektivní selektivní vnímáníNikoli, já pouze vím – na rozdíl od vás – jak to funguje.
Tentokrát tuší, resp. ví docela přesně. Jen nějaký Jirsák tady jako modlitební mlýnek opakuje pořád dokola pár naučených frází, aniž by jim rozumělOmyl. Můžu si myslet, že to má Java vyřešeno stejně dobře jako Erlang atomy. Napíšu si na to i unit testy, ale jako na potvoru tam použiju "malá" čísla. A na produkci se objeví "velká" čísla.Když ono to z vašich komentářů opravdu vypadá, že netušíte, o čem píšete.
Byla to neznalost programátora? Ano. Byla to chyba programátora? Ano.
Programátor si myslel, že konstrukci rozumí a dokonce to i byla pravda - chtěl porovnávat reference (!!!) ). Co neznal, byly internals implementace poolu "malých" hodnot.
Byl jazykem překvapen? Byl. Má ideální jazyk programátora překvapovat? Nemá.
Zopakuje Filip Jirsák po stopadesáté první, že někdo nechápe rozdíl mezi referencí a hodnotou a s poolem to nemá co dělat? Nevíme, ale je to velmi pravděpodobné.
Tohle je nesmírně hloupý příklad, v TeXu není žádný typ zlomek, \frac{a}{b} je AST jako každý jiný, TeX zlomky nijak nevyhodnocujeU zlomků by jeden očekával algebraickou rovnostMyslím, že kdybyste ve zdrojáku napsal 2/4 a TeX by vám to vysázel jako 1/2, velice rychle byste změnil názor.
Co třeba zlomky? Ostatně problém jsou i ty řetězce – jsou řetězce „č“ a „č“ stejné? Jsou? A neměly by stejné řetězce mít stejný počet znaků? První má jeden znak, druhý dva…To jsou z prstu vycucané pseudoproblémy. U zlomků by jeden očekával algebraickou rovnost (u floatu se taky rovná 1.2 a 1.2000000, pokud na ně tedy neudělám napřed toString(), hejže?). Řetězce se většinou porovnávají jednoduše jako pole bytů, tedy typicky unicode (8 nebo 16). Pokud někdo řeší složitou typografii, určitě může použít sofistikovanější knihovnu pracující s glyfy, ligaturami apod., ale každý chápe, že to je speciální případ. Ostatně moderní jazyky to takto mají právě z toho důvodu, že to dává největší smysl.
Spíš jen jinej.Zmínil bych Haskell. Ten mi přijde těžkej, ale matoucí ne.Připomínám předmět vlákna. Bavíme se o ideálním jazyce. Ne o nejhorších možných, na které si člověk může vzpomenout. C, C++, JavaScript jsou všechno možné, jen ne nematoucí.
On existuje jazyk, který je ve všem nematoucí?
To samozřejmě netuším. Ale domnívám se, že některé jazyky jsou na tom výrazně hůře, než jiné. (Nebo narážíš na ten dvojitej zápor? :-P)
Ne, dvojité zápory mi nevadí.Mohl bys to s těmi některými jazyky trochu rozvést? Opravdu mě to zajímá, protože třeba když jsem nad tím přemýšlel, tak v jisté chvíli jsou matoucí jazyky všechny... byť je pravda, že některé třeba ze začátku, jiné později... vyjma Prologu, ale ten dělali Francouzi...
Definice == na řetězcích nebo celých číslech tak, aby se porovnávala hodnota, je intuitivnost sama, nic intuitivnějšího nejde vymyslet. Pokud někdo obhajuje nesmysl, je buď fanatik, nebo prostě mimo. I to blbé Go, které nemá obecné přetěžování operátorů, umí správně porovnat dva řetězce pomocí ==. Porovnání referencí na řetězcích nebo číslech je k ničemu, ostatně rozumné jazyky mají řetězce jako hodnotové typy. Některé tak mají i třeba kolekce (např. Swift), protože to dává ve většině případů větší smysl.V tom ale ten problém není, to tady všichni chápeme, i když se vám třeba zdá, že ne. Problém je v tom poolu konstant, u kterého nikdy nevím (?), jestli tam to číslo je nebo ne.No evidentně to problém je. V tom poolu konstant žádný problém není – když chápete rozdíl mezi hodnotou a referencí, nemůžete hodnotu Integerů nikdy porovnávat pomocí ==. Tím pádem nikdy nezjistíte, jestli tam nějaký pool konstant je nebo není. A když rozdíl mezi hodnotou a referencí nechápete a budete používat == pro porovnání hodnot objektů, bude ten kód špatně bez ohledu na to, zda pool konstant existuje nebo neexistuje.Tak třeba Rust má spoustu operátorů, které nejsou vůbec intuitivní, to je problém sám o sobě. Ale pokud vím, některé operátory se tam dají přetěžovat, včetně ==, což nás vrací zpět na začátek této diskuse – každý operátor rovnosti, který podporuje i něco jiného, než primitivní typy, je kontraintuitivní, protože u složitějších typů vždy narazíme na to, že prostě není jasné, co je to rovnost. No a když je někde == definováno jenom pro primitivní datové typy, pro někoho bude kontraintuitivní, že pro „jednoduché ne-primitivní“ typy nebude definován. Kdyby Java podporovala == jen pro primitivní typy, byl by tu úplně stejný komentář Johnyho, akorát by se podivoval, proč to kompilátor nepřeloží.protože operátory jsou vždy v nějakém případě kontraintuitivníMůžete uvést příklad z Elixiru nebo Rustu?
Kontraintuitivní jsou vlastně i všechny jazyky, které mají operátor / pro celočíselné operandy definován jako celočíselné dělení. V tomto případě je vítězem JavaScript :-)Nebyl jste to vy, kdo mi vyčítal, že mluvím za vás a podsouvám vám něco, co jste netvrdil?Horší řešení nás v této diskusi nezajímají. Stejně dobrá vlastně taky ne, protože z těch stejně dobrých řešení se jedno muselo vybrat – ale pokud máte další stejně dobrá řešení, sem s nimi.
To je ale jedno, ne? Prostě to je oficiálně nedefinované.Jenže on neporovnává dvě čísla, ale dvě reference.V tom ale ten problém není, to tady všichni chápeme, i když se vám třeba zdá, že ne. Problém je v tom poolu konstant, u kterého nikdy nevím (?), jestli tam to číslo je nebo ne.
Lepší by pochopitelně bylo přetížení operátoru ==.Vysvětlil by mi někdo, co se tam děje? Kdyby to bylo něco mimo rozsah 32b intu, tak bych to pochopil. Ale 1024 je furt zatraceně málo.Integer v Javě je objekt, operátor == v Javě porovnává reference. Takže si v tom kódu vytvoří objekt(y), reference na ně jsou uložené do dvou proměnných – a pak zjišťuje, zda ty dvě reference ukazují na stejný objekt.
To, že ty reference někdy mohou ukazovat na stejný objekt je dané tím, že v kódu převádí primitivní typ int (zapsaný literálem 1024) na objektový Integer. Javovský kompilátor má vestavěný mechanismus (nazývaný boxing), který ten převod dělá automaticky. A nepoužívá se při tom konstruktor, ale statická metoda – a ta statická metoda vytváří nové objekty jenom pro větší čísla. U menších čísel má pool instancí a vrací ty instance – aby v paměti neexistovalo tisíc objektů, které v sobě ponesou hodnotu „1“.
Skoro stejně to funguje se Stringy, a v případě Stringů se to učí asi tak ve druhé lekci, že se neporovnávají pomocí == ale pomocí equals(), protože == porovnává reference a mohou existovat dva různé objekty, které obsahují ten samý řetězec.Na upozornění na kontrainutitivnost nějakého prvku jazyka není nic hloupého.Hloupé je to, že uvádí jeden okrajový příklad, a zdá se, že vůbec nechápe podstatu. To, že operátor == v Javě porovnává hodnoty primitivních typů a reference u objektů mi nijak neintuitivní nepřipadá. Napadají mne jen dvě srovnatelně dobrá řešení a žádné lepší. Jedno řešení by bylo pro objekty == udělat jako alias pro equals() a ošetřit null hodnoty. Druhé řešení by bylo == pro objekty úplně zakázat. V obou případech by na porovnání referencí musel existovat nějaká systémová metoda.
To je podobné jako porovnání stringů pomocí ==. Ano, je to zastaralé, nezvyklé a nebezpečné, ale tak se prostě Java chová. Moderní jazyky to už většinou mají lépe.Já nejsem ten, který tady ze sebe dělá hlupáka. Jednou se to rovná, a podruhé ne. Právě ten, kdo chápe ten rozdíl, by nad tím měl kroutit hlavou...No jenže pokud ale mají oba pouze 50 Kč, pak opravdu jo:V jazyce, který umožňuje používat hodnoty i reference, musí programátor znát rozdíl mezi hodnotou a referencí. To je celé, nic dalšího k tomu není potřeba dodávat, kdo ten rozdíl chápe, chápe i hloupost vašich příspěvků.
No jenže pokud ale mají oba pouze 50 Kč, pak opravdu jo:Vždyť jo - pokud má Pepa a Franta jenom 50Kč, tak jsou to socky a pro javisty je socka jako socka.
Nejlepší příklad je IMHO HTTP server a C10M problém (na jednom vlákně), na tom se nejlépe vysvětluje princip async/await, souvisejícího ošetření chyb a vůbec propastný mezi concurrency a parallelism.Tak, jaks to napsal, to vypadá, že async/await je pro tebe jen syntaktický cukr. Ono jde ale hlavně o runtime, třeba v tom JS je obzvlášť důležité, že “blokující” volání kooperativně přenechá jediné vlákno jiné části kódu.Možná jsem to napsal nejasně, ale myslel jsem "čistě z hlediska výkonu". Když to řeknu úplně polopaticky: pokud něco jde dělat paralelně a udělám to paralelně, získám výkon. Žádný jiný magický mechanismus pro získání výkonu tam není.
I u promisů je to to samé - odstartuju HTTP get a zatímco se na pozadí vyřizuje, běží něco jiného. Ale pořád tohle jde jenom pokud to paralelně běžet může. Pokud ne (jsou tam vazby), tak se stejně musí čekat, s tím nikdo nic magicky nenadělá. Pokud jsou v algoritmu vazby, bude i "neblokující" kód blokovaný.
Tak, jaks to napsal, to vypadá, že async/await je pro tebe jen syntaktický cukr. Ono jde ale hlavně o runtime, třeba v tom JS je obzvlášť důležité, že “blokující” volání kooperativně přenechá jediné vlákno jiné části kódu. Je to jako goroutiny s explicitním uváděním async/await (z toho plyne, že to je vesměs zbytečné, implicitně to vypadá ještě lépe). Důležité jsou ty chyby, protože v případě několika do sebe vnořených callbacků nejde udělat návrat z funkce (return by byl jen z lambdy), kdežto v případě linearizace se chyba vrací úplně normálně (jako třeba v Go) nebo normální výjimkou. Mnozí si bohužel myslí, že to explicitní uvádění je rozumné, už to proniklo i do C++ (co_await).Tady jsi to nepochopil ty, u async/await nejde o promisy, ale korutiny.Tohle nechápu, můžeš to nějak rozvést?V případě NIO nebo třeba semaforů je to skutečně “magický mechanismus”. Výhoda oproti callbackům jsou kromě přehlednosti rozumně ošetřitelné chyby.Ok, rozumně ošetřitelné chyby můžou být, to je spíš interní věc jazyka. Ale jinak na tom nic magického není, pořád je to starý dobrý kooperativní multitasking, navíc s jenom potenciální paralelizací. Můžeš si to klidně napsat ručně, pokud máš k dispozici nějaký mechanismus, na kterém se dá stavět (multithreading, green threads, coroutiny, ...)
Jediná výhoda async/await je v tom, že to ručně psát nemusíš a překladač to udělá za tebe.
V Javascriptu si nejsem jisty, zda Promise predstavuje stejny mechanismus jako Coroutiny v C#.Je to stejné.
Tady jsi to nepochopil ty, u async/await nejde o promisy, ale korutiny. V případě NIO nebo třeba semaforů je to skutečně “magický mechanismus”. Výhoda oproti callbackům jsou kromě přehlednosti rozumně ošetřitelné chyby.To by se u normalniho stavoveho stroje s await async nemelo stat, protoze tam bezi jenom jeden thread.Pokud pisete komponentu, kde vam chodi paralelne requesty, tak mate zparalelizovane uz ty requesty. Nepotrebujete paralelizovat i ukoly v ramci jednotlivych requestu, to je nesmysl.Protoze k tomu abyste z toho opravdu dokazal tezit, tak potrebujete mit drtivou vetsinu metod await async - a proto nemuzete vyrabet novy thread pokade kdyz se vola nejaka metoda.
Neber si to osobně, ale tohle je dost odstrašující příklad přístupu k asynchronnímu programování.
1. chci používat jazyk s mutabilními strukturami (navíc OOP!)
2. async/paralelizaci si představuju tak, že všude možně prsknu async a await
3. očekávám, že to po mně nebude nic chtít, protože to bude "jakože běžet v jednom vlákně"
4. díky tomu, že mám sem tam ten async a await, to bude automagicky škálovat
Prvně by asi nebylo od věci si uvědomit, že
1. async/await není nic jiného než syntaktický cukr, který z programátora jenom snímá nutnost používat napřímo promisy (nebo jiný podobný mechanismus) a překladač je tam doplňuje sám. Takže všechno, co platí pro promisy (popř. ten jiný podobný mechanismus), platí i pro async/await kód.
2. neexistuje žádný zázračný "neblokující kód", který automagicky vede k vyššímu výkonu. Celý je to zcela prostý: pokud mám operace A, B, C, kde A a B jsou vzájemně nezávislé a C je závislé na A i B, tak se hodí A a B spustit zaráz a C až potom, co A i B doběhnou. Zázračný "neblokující kód" je samozřejmě při čekání na A+B blokovanýTo je celý princip a je úplně jedno, jaká syntaktická wifikundace se použije k jeho zápisu.
3. díky async/await zápisu sice kód vypadá jako seriový (synchronní), ale to jenom vypadá a pořád je potřeba myslet na to, že smyslem té celé srandy je to, aby A a B mohly běžet potenciálně paralelně, protože to jediné je zdrojem toho potenciálního nárůstu výkonu, žádnej jinej magickej zrychlovací mechanismus tam není a být nemůže.
V Javě jsou těch GC tři p....e, ovšem to nemění nic na tom, že to kopírování je v praxi zbytečné.Ne každý tracing GC kopíruje, třeba v Go je normální alokátor a GC (tricolor) běží na pozadí a barví si objekty. A světe div se, je efektivnější než ten kopírující v Javě nebo .NET.V jave sa barvi konkurentne tiez, ci? Navyse sa aj kopirovanie deje konkurentne a aplikacne thready pomahaju s kopirovanim.
Navyse sa kopirovane objekty mozu aj subezne mutovat. dobre nie? Jeden by povedal, ze v tom je cierna magia a to je len klasicky trancing GC, teda hned dva.