Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: fortran1986 05. 05. 2019, 17:47:37
-
Predstavte si že vytvárate nový programovací jazyk presne podľa vašich predstáv. Máte na to team odborníkov a pripravujete im zadanie:
1. Akú by mal mať váš jazyk filozofiu, pradigmu?
2. Čo by ste okopírovali z iných jazykov?
3. Čo by ste naopak úplne vynechali?
4. Čím by bol váš jazyk inovatívny? Aké nové vlastnosti ktoré vám chýbajú inde by mal mať váš jazyk?
5. Akým nedostatkom s ktorými sa ako programátor strtávate by ste sa chceli vyhnúť?
-
Funkcionalni jazyky jsou pekna hracka, ale jsou daleko od hardware a lide jim taky neprijdou vzdycky na chut.
Ja mam rad C a ted se mi libi Go, jednoduche, blizko hw, Go se snazi vychytat veci s pameti a paralelizaci.
C++ a Java me zivi, ale to jsou obludy a silene ekosystemy kolem toho a na kazdou blbinu je tam komise a standardizacni postup.
Kdyz si vezmu matematiky a fyziky tak je fortran, matlab pro ne super. Jednoducha prace s maticemi a takrka matematicky zapis.
Nebo jazyk APL, silene nadherna syntaxe, ale taky ne pro kazdeho cloveka.
Takze za me C a Go.
-
- Je potřeba vytvářet nový jazyk?
- Nejdřív je potřeba vytvořit ideálního programátora.
-
Pekny popis na blogu software samuraj: remcani proti jave.
-
- Je potřeba vytvářet nový jazyk?
- Nejdřív je potřeba vytvořit ideálního programátora.
Pockame si na AI :-)
-
Cim idealnejsi jazyk je tim vice se podoba lispu.
-
Nejdřív bych si ujasnil, k čemu má vlastně ten jazyk sloužit. Pak bych řešil, jestli je kvůli tomu nutné vytvářet nový jazyk. Teprve na základě toho bych pak případně řešil nějaký tým odborníků a zadání.
-
Cim idealnejsi jazyk je tim vice se podoba lispu.
Chtel bych na nizke urovni videt kod pro kernel v lispu :-)
Kacirsky dotaz: jsou funkcionalni jazyky opravdu efektivnejsi? Mi se Lisp a dalsi libi, ale prakticky jsem je nevyuzil.
-
A jeste, pro funkcionalni jazyky potrebujete slozitejsi/vymakanejsi virtualni stroj nebo kompiler, neni tak?
-
Cim idealnejsi jazyk je tim vice se podoba lispu.
Chtel bych na nizke urovni videt kod pro kernel v lispu :-)
Kacirsky dotaz: jsou funkcionalni jazyky opravdu efektivnejsi? Mi se Lisp a dalsi libi, ale prakticky jsem je nevyuzil.
https://en.wikipedia.org/wiki/Movitz (https://en.wikipedia.org/wiki/Movitz) ;-)
Definuj efektivnejsi. Podle me jde v lispu lepe vyjadrit problem ktery resim.
-
Movitz, tam bude hafo assembleroveho kodu ne?!!
-
Cim idealnejsi jazyk je tim vice se podoba lispu.
Chtel bych na nizke urovni videt kod pro kernel v lispu :-)
Kacirsky dotaz: jsou funkcionalni jazyky opravdu efektivnejsi? Mi se Lisp a dalsi libi, ale prakticky jsem je nevyuzil.
Lisp, který běží přímo na HW, je kernelem.
-
Movitz, tam bude hafo assembleroveho kodu ne?!!
Záleží na tom, zda je kernel napsán v assembleru, C nebo ve Fortranu? To jsou jazyky, které slouží převážně k napsání kompilátorů vyšších jazyků a následně jejich význam upadá - tedy až na ten Fortran, který je stále významným jazykem.
-
Pro me je nejdulezitejsi velikost komunity a mnozstvi dostupnych knihoven. Sebelepsi jazyk je k nicemu, kdyz v nem musite znovuvynalezat kolo.
-
Zkousel jsm si to predstavit a zjistil jsem, ze na programovaci jazyk tak akorat z vysoka kaslu a kalim, dulezita je knihovna co je pod tim, co vsechno v ni je a jak dobre se to pouziva. Za me vede Java a C#, kde Java je pro ty chytrejsi, takze Java 8) Akorat co me sejre je, ze v Jave nejsou moc neblokujici verze metod, ale to je taky otazka jestli to je problem, protoze treba POSIX sockety jsou blokujici a vlastne nevim jak se na urovni psani C kodu da na Linuxu udelat neblokujici Socket, jestli to teda vubec jde. Protoze treba design Node.js je neblokujici, ale k cemu ti to je, kdyz na urovni operacniho systemu to blokujici je. To ze ti Node.js nezablokuje thred kdyz posilas request neznamena, ze se pod tim nevyrobi dalsi thread. A v tom pripade toto umi i Java. Jaky design v tomhle ma .NET a jestli Windows dokazi udelat neblokujici Socket, to nevim.
-
Zkousel jsm si to predstavit a zjistil jsem, ze na programovaci jazyk tak akorat z vysoka kaslu a kalim, dulezita je knihovna co je pod tim, co vsechno v ni je a jak dobre se to pouziva. Za me vede Java a C#, kde Java je pro ty chytrejsi, takze Java 8) Akorat co me sejre je, ze v Jave nejsou moc neblokujici verze metod, ale to je taky otazka jestli to je problem, protoze treba POSIX sockety jsou blokujici a vlastne nevim jak se na urovni psani C kodu da na Linuxu udelat neblokujici Socket, jestli to teda vubec jde. Protoze treba design Node.js je neblokujici, ale k cemu ti to je, kdyz na urovni operacniho systemu to blokujici je. To ze ti Node.js nezablokuje thred kdyz posilas request neznamena, ze se pod tim nevyrobi dalsi thread. A v tom pripade toto umi i Java. Jaky design v tomhle ma .NET a jestli Windows dokazi udelat neblokujici Socket, to nevim.
nodejs opravdu nevytvari vlakno pro kazdou cekajici operaci.
-
Zkousel jsm si to predstavit a zjistil jsem, ze na programovaci jazyk tak akorat z vysoka kaslu a kalim, dulezita je knihovna co je pod tim, co vsechno v ni je a jak dobre se to pouziva. Za me vede Java a C#, kde Java je pro ty chytrejsi, takze Java 8) Akorat co me sejre je, ze v Jave nejsou moc neblokujici verze metod, ale to je taky otazka jestli to je problem, protoze treba POSIX sockety jsou blokujici a vlastne nevim jak se na urovni psani C kodu da na Linuxu udelat neblokujici Socket, jestli to teda vubec jde. Protoze treba design Node.js je neblokujici, ale k cemu ti to je, kdyz na urovni operacniho systemu to blokujici je. To ze ti Node.js nezablokuje thred kdyz posilas request neznamena, ze se pod tim nevyrobi dalsi thread. A v tom pripade toto umi i Java. Jaky design v tomhle ma .NET a jestli Windows dokazi udelat neblokujici Socket, to nevim.
nodejs opravdu nevytvari vlakno pro kazdou cekajici operaci.
1 POSIX Socket = 1 vyzrany thread
100 paralelnich bezicich requestu pres POSIX Socket = 100 vyzrany threadu
Pres to nejede vlak
To se tyka OS, je uplne jedno co dela Node.js.
Vyjimka by mohlo byt akorat to, ze uz existuje nejaky neblokujici Socket na urovni OS.
-
Ale je mozne, ze by to nejak slo udelat pres epoll http://man7.org/linux/man-pages/man7/epoll.7.html
Jo pouziva to na Linuxu epoll, overeno
https://stackoverflow.com/questions/38130102/is-libuv-under-the-hood-use-epoll-or-select2-in-unix
No takze za me, na programovaci jazyk dlabu, zajima me, aby moje platforma podporovala takoveto veci.
-
Ale je mozne, ze by to nejak slo udelat pres epoll http://man7.org/linux/man-pages/man7/epoll.7.html
Jo pouziva to na Linuxu epoll, overeno
https://stackoverflow.com/questions/38130102/is-libuv-under-the-hood-use-epoll-or-select2-in-unix
No takze za me, na programovaci jazyk dlabu, zajima me, aby moje platforma podporovala takoveto veci.
to prave java moc nepodporuje.
-
Predstavte si že vytvárate nový programovací jazyk presne podľa vašich predstáv. Máte na to team odborníkov a pripravujete im zadanie:
1. Akú by mal mať váš jazyk filozofiu, pradigmu?
2. Čo by ste okopírovali z iných jazykov?
3. Čo by ste naopak úplne vynechali?
4. Čím by bol váš jazyk inovatívny? Aké nové vlastnosti ktoré vám chýbajú inde by mal mať váš jazyk?
5. Akým nedostatkom s ktorými sa ako programátor strtávate by ste sa chceli vyhnúť?
1. programovaci jazyk
2. python
3. nic
4. python
5. ziadnym
suma sumarum python
-
Ale je mozne, ze by to nejak slo udelat pres epoll http://man7.org/linux/man-pages/man7/epoll.7.html
Jo pouziva to na Linuxu epoll, overeno
https://stackoverflow.com/questions/38130102/is-libuv-under-the-hood-use-epoll-or-select2-in-unix
No takze za me, na programovaci jazyk dlabu, zajima me, aby moje platforma podporovala takoveto veci.
to prave java moc nepodporuje.
Alaae podporuje, ma neblokujici sockety a asynchronni jdbc, je to soucasti standard balicko "nio" - nonblocking IO. Akorat nad tim neblokujici JDBC zacali vyvijet teprve ani ne pred 2 roky a jeste z toho neni standard a nevim o tom, ze by to uz podporal treba Spring.
-
1 POSIX Socket = 1 vyzrany thread
100 paralelnich bezicich requestu pres POSIX Socket = 100 vyzrany threadu
Pres to nejede vlak
To se tyka OS, je uplne jedno co dela Node.js.
Vyjimka by mohlo byt akorat to, ze uz existuje nejaky neblokujici Socket na urovni OS.
Ale je mozne, ze by to nejak slo udelat pres epoll http://man7.org/linux/man-pages/man7/epoll.7.html
Epoll je vlastně jen rychlejší select nebo poll.
A stejně jako s epollem se s nimi dá obsloužit více socketů z jednoho vlákna, ideálně v neblokujícím režimu - což většina rozumných OS podporuje(i když si nejsem jist zda neblokující režim je v čistém posixu).
-
Zkousel jsm si to predstavit a zjistil jsem, ze na programovaci jazyk tak akorat z vysoka kaslu a kalim, dulezita je knihovna co je pod tim, co vsechno v ni je a jak dobre se to pouziva. Za me vede Java a C#, kde Java je pro ty chytrejsi, takze Java 8) Akorat co me sejre je, ze v Jave nejsou moc neblokujici verze metod, ale to je taky otazka jestli to je problem, protoze treba POSIX sockety jsou blokujici a vlastne nevim jak se na urovni psani C kodu da na Linuxu udelat neblokujici Socket, jestli to teda vubec jde. Protoze treba design Node.js je neblokujici, ale k cemu ti to je, kdyz na urovni operacniho systemu to blokujici je. To ze ti Node.js nezablokuje thred kdyz posilas request neznamena, ze se pod tim nevyrobi dalsi thread. A v tom pripade toto umi i Java. Jaky design v tomhle ma .NET a jestli Windows dokazi udelat neblokujici Socket, to nevim.
nodejs opravdu nevytvari vlakno pro kazdou cekajici operaci.
1 POSIX Socket = 1 vyzrany thread
100 paralelnich bezicich requestu pres POSIX Socket = 100 vyzrany threadu
Pres to nejede vlak
To se tyka OS, je uplne jedno co dela Node.js.
Vyjimka by mohlo byt akorat to, ze uz existuje nejaky neblokujici Socket na urovni OS.
Každý OS na to má svůj mechanismus, například BSD má kqueue a Windows IOCP. Dělat pro milion requestů milion vláken by asi nebyl dobrý nápad.
-
Cim idealnejsi jazyk je tim vice se podoba lispu.
Chtel bych na nizke urovni videt kod pro kernel v lispu :-)
Kacirsky dotaz: jsou funkcionalni jazyky opravdu efektivnejsi? Mi se Lisp a dalsi libi, ale prakticky jsem je nevyuzil.
Japonci psali kernely v Prologu, tak v Lispu by to šlo taky.
-
Nejdřív bych si ujasnil, k čemu má vlastně ten jazyk sloužit. Pak bych řešil, jestli je kvůli tomu nutné vytvářet nový jazyk. Teprve na základě toho bych pak případně řešil nějaký tým odborníků a zadání.
Tak nějak. Jazyk je nástroj. Jeho vlastnosti vycházejí převážně z toho, jaký problém se zrovna řeší. (A to opomíjím, že výběr programovacího jazyka je v reálu málo technické a hodně manažerské rozhodnutí.) Chtít ideální jazyk bez přesnější specifikace problému je jako chtít ideální dopravní prostředek, nebo kuchyňský spotřebič.
-
Nakonec se v každém jazyku dostanu k zadání vytvořit formulář, zvalidovat políčka, kliknout na Ok a uložit do SQL. Záleží jen, jak dlouhá cesta k tomu vede. Výsledkem by měla být malá přenosná aplikace.
-
Každý jazyk má omezenou "ideální oblast", kde bude excelovat, čím dále od ní bude, tím to více dře. Na jedné straně je vysokoúrovňový (v zásadě deklarativní, lidsky srozumitelný) popis problému a na druhé optimalizovaný výpočet blízko konkrétnímu železu. Různé chytré kompilátory a "bezplatné abstrakce" se snaží tu vzdálenost překlenout, ale zatím se nezdá, že by to šlo dokonale.
Ideální programovací prostředí je podle mě takové, které umožní ponechat ten deklarativní popis problému co nejširší a chytře vkládat konkrétní kousky, které řeší tu nízkoúrovňovou optimalizaci, aniž by ohrozily bezpečnost a správnost výpočtu. Z konkrétních jazyků bych řekl, že má správným směrem nakročeno třeba Rust, ale uvidíme, kam se to bude vyvíjet.
-
Predstavte si že vytvárate nový programovací jazyk presne podľa vašich predstáv.
Jde o to, pro koho. Pro nepříliš zdatné programátory, tedy přes 95% IT audience? Ti mají Javu a PHP. Jinak mají smysl jen úzce specializované jazyky, v CERNu si třeba vytvořili speciální jazyk pro HPC. To ale není téma pro toto fórum.
-
Predstavte si že vytvárate nový programovací jazyk presne podľa vašich predstáv.
Jde o to, pro koho. Pro nepříliš zdatné programátory, tedy přes 95% IT audience? Ti mají Javu a PHP. Jinak mají smysl jen úzce specializované jazyky, v CERNu si třeba vytvořili speciální jazyk pro HPC. To ale není téma pro toto fórum.
CERN si sice jazyk napsal(nekolik...), ale datovy analyzy dela na Sparku v pythonu, v cem jinym. napsali si svoji phy-extenzi dataframu RDataFrame, ktery ma ruzne featury, ktere potrebuji, a je napsany v C++ s python bindingem, vse zastresene jupyterem.
Ohledne HPC, ucim se ted Legion(Stanford) ve kterem je napsany LuxGraph (gpu distribuovany graph engine), ale stejne si pro nej delam python(boost)/scala(javacpp) interfacem pro IO.
Pokladat Javu za jazyk pro neprilis zdatne programatory muze napsat jen nezdatny programator :-)
-
Kdyz si vezmu matematiky a fyziky tak je fortran, matlab pro ne super. Jednoducha prace s maticemi a takrka matematicky zapis.
Prosím ne. K Fortanu se nehodlám moc vyjadřovat, jen uvedu, že většina fortranového kódu, co jsem kdy viděl, byla neuvěřitelně zprasená, čímž jsem si k němu asi vytvořil doživotní odpor. Takže na „tvrdou“ numeriku používám C.
A co se týče matlabu, ten se podle mne taky drží už jen ze setrvačnosti, bo ta „přirozená“ syntaxe stejně bere za své, když potřebujete pracovat např. s tensory vyššího řádu. Zlaté NumPy, které je navíc na rozdíl od matlabu otevřené.
-
... fortran, matlab ...
Prosím ne. ...
Už jsem to tu kdysi zmiňoval. Kamarád něco řešil v Matlabu - a trvalo to dlouho. Příliš dlouho. Tak to přepsal do Fortranu a bylo to rychlejší. Neřekl mi sice kolikrát, ale prý to číslo mělo pět nul ;D
Je tedy poněkud mladší a změkčilejší, takže to byl F90, já bych dal přednost FORTRANu IV.
-
Prosím ne. K Fortanu se nehodlám moc vyjadřovat, jen uvedu, že většina fortranového kódu, co jsem kdy viděl, byla neuvěřitelně zprasená, čímž jsem si k němu asi vytvořil doživotní odpor. Takže na „tvrdou“ numeriku používám C.
Tvrdou numeriku má C neuvěřitelně zprasenou, takže raději ten Fortran. Ovšem v dnešní době jdeme do vyšších jazyků a v nich povede Python.
-
Ideální jazyk? Bude to sice znít masochisticky, nicméně za mě
- Vyjít konceptem a syntaxí z Pythonu
- Odstranit GIL
- Přidat silnou typovost ala cpp
- Odstranit garbage collector, přidat ruční správu paměti a destruktory
- Udělat jako kompilovaný jazyk, jehož výsledkem bude klasická binárka běžící rovnou na železe
-
Ideal je co neco zpusob pomocnika, kteryho ma k dispozici Iron Man. Zadani se rovnou materializuje a nevyzaduje vysedavani u kompu at all! Vystup je nejen informacni, ale i materialni (pokud je potreba)
Infrastruktura skladajici se z pocitace, obrazovky a klavesnice, jazyku je vlastne uplne strasne komunikacni rozhrani, ve kterem si muzeme skladat modely myslenek pro hledani odpovedi(ani ne tak reseni :-)) ), at uz je tim pocet emitovanych castic, nebo delani loginu do systemu, nebo pocitani auticek na fotce, cokoli. Potrebujeme vytvorit ne jazyk, ale pomocnika, nejlepe pomocniky, kteri si vytvori vlastni jazyk, ale budou rozumnet nasemu jazyku, nejlepe jeste telepaticky, ale tak to muze umet verze 2-ALFA. A nase myslenkove uvahy se muzou posunout na jinam... :-))
Takze me zajima jaky jazyk by byl nejlepsi pro navrh takoveho pomocnika.
Jinak ten clanek o Jave od Software Samuraje je nadherny :-)
za me dnes vede Go a python (primocarost), A v GCC vystrcila ruzek podpora D (o nem jen sem tam ctu, ale nenapsal jsem v nem jeste ani return :-)
-
- Odstranit garbage collector, přidat ruční správu paměti a destruktory
Brrr, zadna rucni sprava... its just burden, ja taky neresim, co si mam pamatovat a co zapomenout, zapomene se to samo.
-
Brrr, zadna rucni sprava... its just burden, ja taky neresim, co si mam pamatovat a co zapomenout, zapomene se to samo.
Do té doby, než jsem objevil Qt framework a jeho metodu všechny data řadíme do stromu a když jeden node stromu smažeme, tak se sám uklidí binec pod tím, jsem si to taktéž myslel, nicméně při šikovném návrhu jednotlivých tříd ten garbage collector prostě není potřeba
-
Ideální jazyk? Bude to sice znít masochisticky, nicméně za mě
- Vyjít konceptem a syntaxí z Pythonu
Jo, tohle zní hodně masochisticky.
- Přidat silnou typovost ala cpp
C++ nemá silnou typovost.
- Udělat jako kompilovaný jazyk, jehož výsledkem bude klasická binárka běžící rovnou na železe
Líbí se mi Haskell, respektive interaktivní ghci a to, jak je to udělané. :)
-
Ideální jazyk? Bude to sice znít masochisticky, nicméně za mě
- Vyjít konceptem a syntaxí z Pythonu
- Odstranit GIL
- Přidat silnou typovost ala cpp
- Odstranit garbage collector, přidat ruční správu paměti a destruktory
- Udělat jako kompilovaný jazyk, jehož výsledkem bude klasická binárka běžící rovnou na železe
mohl by se ti líbit Nim https://github.com/nim-lang/Nim/wiki/Nim-for-Python-Programmers
-
Ideal je co neco zpusob pomocnika, kteryho ma k dispozici Iron Man. Zadani se rovnou materializuje a nevyzaduje vysedavani u kompu at all! Vystup je nejen informacni, ale i materialni (pokud je potreba)
Infrastruktura skladajici se z pocitace, obrazovky a klavesnice, jazyku je vlastne uplne strasne komunikacni rozhrani, ve kterem si muzeme skladat modely myslenek pro hledani odpovedi(ani ne tak reseni :-)) ), at uz je tim pocet emitovanych castic, nebo delani loginu do systemu, nebo pocitani auticek na fotce, cokoli. Potrebujeme vytvorit ne jazyk, ale pomocnika, nejlepe pomocniky, kteri si vytvori vlastni jazyk, ale budou rozumnet nasemu jazyku, nejlepe jeste telepaticky, ale tak to muze umet verze 2-ALFA. A nase myslenkove uvahy se muzou posunout na jinam... :-))
Takze me zajima jaky jazyk by byl nejlepsi pro navrh takoveho pomocnika.
Jinak ten clanek o Jave od Software Samuraje je nadherny :-)
za me dnes vede Go a python (primocarost), A v GCC vystrcila ruzek podpora D (o nem jen sem tam ctu, ale nenapsal jsem v nem jeste ani return :-)
Takovy pomocnik uz existuje, vymysleli ho uz davno v Micosoftu, jmenuje se Sponka.
-
Ideální jazyk? Bude to sice znít masochisticky, nicméně za mě
- Vyjít konceptem a syntaxí z Pythonu
- Odstranit GIL
- Přidat silnou typovost ala cpp
- Odstranit garbage collector, přidat ruční správu paměti a destruktory
- Udělat jako kompilovaný jazyk, jehož výsledkem bude klasická binárka běžící rovnou na železe
Python 3 je dobrý základ.
Odstranit GIL by se hodilo.
Silné typy už python má. Statické anotace už také podporuje.
Ruční správu paměti - ee.
Kompilovat Python v podstatě už jde, viz Cython.
-
- Odstranit garbage collector, přidat ruční správu paměti a destruktory
Pokud jsem si všiml, tak destruktory fungují korektně jen v PHP. Spouští se ihned po uvolnění objektu. V ostatních jazycích je to slepeno dohromady s GC - destruktory neplní řádně svou funkci a proto je s nimi tolik potíží.
-
Pokud jsem si všiml, tak destruktory fungují korektně jen v PHP. Spouští se ihned po uvolnění objektu. V ostatních jazycích je to slepeno dohromady s GC - destruktory neplní řádně svou funkci a proto je s nimi tolik potíží.
No nevím, ale v cpp jsem divné chování destruktorů nikdy nezaznamenal, zatím se mi vždy spustil bezprostředně po zavolání delete nebo když daný objekt má zmizet ze stacku. Pravdou je, že u jazyků používajících garbage collector tomu může být jinak, nicméně to bude asi jeden z důvodů, proč v Pythonu destruktory nejsou (měl by se spustit s během GC, což je nedefinovaný okamžik).
-
Pokud jsem si všiml, tak destruktory fungují korektně jen v PHP. Spouští se ihned po uvolnění objektu. V ostatních jazycích je to slepeno dohromady s GC - destruktory neplní řádně svou funkci a proto je s nimi tolik potíží.
No nevím, ale v cpp jsem divné chování destruktorů nikdy nezaznamenal, zatím se mi vždy spustil bezprostředně po zavolání delete nebo když daný objekt má zmizet ze stacku. Pravdou je, že u jazyků používajících garbage collector tomu může být jinak, nicméně to bude asi jeden z důvodů, proč v Pythonu destruktory nejsou (měl by se spustit s během GC, což je nedefinovaný okamžik).
Ruční volání destruktorů nebo třeba zavírání souborů by mě už nebavilo. Proč bych to měl hlídat?
-
Pokud jsem si všiml, tak destruktory fungují korektně jen v PHP. Spouští se ihned po uvolnění objektu. V ostatních jazycích je to slepeno dohromady s GC - destruktory neplní řádně svou funkci a proto je s nimi tolik potíží.
No nevím, ale v cpp jsem divné chování destruktorů nikdy nezaznamenal, zatím se mi vždy spustil bezprostředně po zavolání delete nebo když daný objekt má zmizet ze stacku. Pravdou je, že u jazyků používajících garbage collector tomu může být jinak, nicméně to bude asi jeden z důvodů, proč v Pythonu destruktory nejsou (měl by se spustit s během GC, což je nedefinovaný okamžik).
Ruční volání destruktorů nebo třeba zavírání souborů by mě už nebavilo. Proč bych to měl hlídat?
V cpp zadne rucni volani destruktoru neni potreba. Akorat lze presne zjistit kouknutim do kodu, kdy se tak stane.
-
Ruční volání destruktorů nebo třeba zavírání souborů by mě už nebavilo. Proč bych to měl hlídat?
V cpp zadne rucni volani destruktoru neni potreba. Akorat lze presne zjistit kouknutim do kodu, kdy se tak stane.
Takže se zánikem posledního deskriptoru na objekt se automaticky zavolá jeho destruktor? Java se má co učit, neboť tohle neumí.
-
Me na CPP stve, a doted mi nikdo nevysvetlil proc to tak je, neco cemu se da rika zbastardizovani objektu. To se vam tak snazim psat tak, aby se alokovalo na stacku, jenze na stacku neni proste mozne udelat polymorfismus. Kdyz se o to pokusim, tak c++ zbastardizuje objekt do predka a vytvori bastarda. Uz nevim jak se tomu presne rika, ja tomu rikam bastardizace.
A dalsi vec co nechapu proc to C++ dela je, ze kdyz chci vyuzivat jen stacku, tak pokud vracim z nejake funkce objekt na stacku, tak C++ ten objekt veme a cely ho prekopiruje jinam - nechapu proc, dyt je to neefektivni.
Takze z toho duvodu moc nechapu vyhody psani na stacku. Mohl bych se toho drzet ledaze bych nepouzival OOP paradigma, ale to uz jsem zase ve sfere kockopsovani.
Dale me na C++ dost vytaci hlavickove soubory, to je proste z hlediska OOP paradigmatu totalni kockopsovina.
A uplne nejvic me stve, ze to neni ani poradne zaplacene, tak proc se s tim stvat.
-
Dale me na C++ dost vytaci hlavickove soubory, to je proste z hlediska OOP paradigmatu totalni kockopsovina.
Proč? Buď píšu do hlavičkového "vše", takže je to "jedno". Nebo mám v hlavičkovém jen rozhraní a potom mi to přijde přehlednější... ale možná si jen nerozumíme. :)
-
A dalsi vec co nechapu proc to C++ dela je, ze kdyz chci vyuzivat jen stacku, tak pokud vracim z nejake funkce objekt na stacku, tak C++ ten objekt veme a cely ho prekopiruje jinam - nechapu proc, dyt je to neefektivni.
Ono to jinak ani nejde, protože při návratu je ten stack zlikvidován.
-
Ja bych osobn e byl pro aby na stacku byly jen reference, tj. odkazy na adresy. Objekty, pole, cokoliv je vic nez typ se systemovou velikosti adresy (char, int32, int64) by uz musely byt na heapu a hlidane smart pointerem nebo referenci, zadne obludy na stacku. To jen k c++.
C++ uz nemam rad, mnohem radeji bych delal v Go. Smart pointery, template, STL, Boost, template SFINAE, = by melo byt automaticky move, hafo typu referenci, &, & &.
-
Ja bych osobn e byl pro aby na stacku byly jen reference, tj. odkazy na adresy. Objekty, pole, cokoliv je vic nez typ se systemovou velikosti adresy (char, int32, int64) by uz musely byt na heapu a hlidane smart pointerem nebo referenci, zadne obludy na stacku. To jen k c++.
To lze udělat, ale bude to nutně (a zbytečně) pomalejší. Taky by šlo proti filozofii C++.
-
Ja bych osobn e byl pro aby na stacku byly jen reference, tj. odkazy na adresy. Objekty, pole, cokoliv je vic nez typ se systemovou velikosti adresy (char, int32, int64) by uz musely byt na heapu
Zrovna Go dává na zásobník kde co, a jak šlape - překladač sám pozná, co musí být na haldě, díky čemuž toho GC nemá typicky moc na práci. Zrovna tohle třeba Javě bolestně chybí.
-
Predstavte si že vytvárate nový programovací jazyk presne podľa vašich predstáv.
Jde o to, pro koho. Pro nepříliš zdatné programátory, tedy přes 95% IT audience? Ti mají Javu a PHP. Jinak mají smysl jen úzce specializované jazyky, v CERNu si třeba vytvořili speciální jazyk pro HPC. To ale není téma pro toto fórum.
Pokladat Javu za jazyk pro neprilis zdatne programatory
To byl Goslingův deklarovaný cíl. Až dospěješ, tak ti to dojde.
-
A dalsi vec co nechapu proc to C++ dela je, ze kdyz chci vyuzivat jen stacku, tak pokud vracim z nejake funkce objekt na stacku, tak C++ ten objekt veme a cely ho prekopiruje jinam - nechapu proc, dyt je to neefektivni.
Pokud vracis objekt vytvoreny na stacku, tak se to nedeje, vetsina objektu se vytvari tam kde je potreba. Takze ze stacku se nic nekopiruje.
Dale me na C++ dost vytaci hlavickove soubory, to je proste z hlediska OOP paradigmatu totalni kockopsovina.
To je bohuzel z historickych duvodu a z duvodu kompatibility s C. Snad to moduly ted zmeni. Ted se to muselo ruzne obchazet.
A uplne nejvic me stve, ze to neni ani poradne zaplacene, tak proc se s tim stvat.
Zaplacene je to velmi dobre, pokud neco umis, pokud jsi standardni noob, tak ti samozrejme nikdo sypat zlato nebude.
-
Ruční volání destruktorů nebo třeba zavírání souborů by mě už nebavilo. Proč bych to měl hlídat?
V cpp zadne rucni volani destruktoru neni potreba. Akorat lze presne zjistit kouknutim do kodu, kdy se tak stane.
Takže se zánikem posledního deskriptoru na objekt se automaticky zavolá jeho destruktor? Java se má co učit, neboť tohle neumí.
Ano, pokud se pouziva moderni cpp(od 11ky, i kdyz i drive byly smart pointry ale s problemama.) Samozrejme je to zpetne kompatibilni, takze kdyz se vylozene chces strilet do nohy a ignorujes guideliny, ktere radi nepouzivat stare konstrukce nutne z duvodu zpetne kompatibility, tak si tu nohu ustrelit porad muzes.
-
Me na CPP stve, a doted mi nikdo nevysvetlil proc to tak je, neco cemu se da rika zbastardizovani objektu. To se vam tak snazim psat tak, aby se alokovalo na stacku, jenze na stacku neni proste mozne udelat polymorfismus. Kdyz se o to pokusim, tak c++ zbastardizuje objekt do predka a vytvori bastarda. Uz nevim jak se tomu presne rika, ja tomu rikam bastardizace.
Říká se tomu slicing. Je to důsledek toho, že v kontextu kdy neznáš kompletní typ objektu, tak je hodně těžké ho kopírovat. C++ zvolilo jedno z možných omezených řešení, jiné jazyky volí jiná omezení. Dobré řešení AFAIK není.
Problém není polymorfismus ale právě kopírování. Kód :
void foo( const Base & );
//...
Derived aaa;
foo( aaa );
funguje ok bez jakékoliv "bastardizace". Problém nastává třeba u
Base foo()
{
Derived temp;
return temp;
}
V místě, kde foo volám, o typu Derived nevím vůbec nic. Může to být v modulu, který se kompiloval v době, kdy Derived ještě vůbec neexistoval. Aby volající kód mohl kopírovat neznámý odvozený typ, musel by mít k dispozici něco jako "virtuální kopírovací konstruktor" + podporu objektů neznámého typu na zásobníku. To druhé bohužel hodně omezí optimalizátor v rozletu.
Spousta jazyků to řeší stylem všechno je na heapu a vůbec nic se nekopíruje. V porovnání se zásobníkem je halda obecnější a pomalejší. A třeba v hard realtime systémech je dynamická alokace na haldě hodně velký problém. Takže C++ šlo cestou že to dovolí, protože to programátor občas potřebuje.
Dobrý zvyk v C++ je, že pokud píšu "interface" nebo spíš třídu s virtuálními metodami, tak zakážu kopírování abych si nemohl naběhnout na vidle.
-
Dobrý zvyk v C++ je, že pokud píšu "interface" nebo spíš třídu s virtuálními metodami, tak zakážu kopírování abych si nemohl naběhnout na vidle.
ja jsem to uplne 100% nepochopil, ale doptavat uz se nebudu, tohle co pisete to uz je proste fakt moc. Ja nikdy proste v C++ delat nechci, protoze to je hruza. To si to radeji napisu v C a budu delat polymorfismus pres pointry na funkce.
-
Cim idealnejsi jazyk je tim vice se podoba lispu.
Chtel bych na nizke urovni videt kod pro kernel v lispu :-)
Kacirsky dotaz: jsou funkcionalni jazyky opravdu efektivnejsi? Mi se Lisp a dalsi libi, ale prakticky jsem je nevyuzil.
podivejte se treba na videa od baggers na Twitchi nebo YT. Programuje 3d grafiku v Common Lispu. https://www.youtube.com/channel/UCMV8p6Lb-bd6UZtTc_QD4zA. Zajimava je hlavne jeho produktivita prace oproti streamerum programujicim v C/C++.
-
ja jsem to uplne 100% nepochopil, ale doptavat uz se nebudu, tohle co pisete to uz je proste fakt moc. Ja nikdy proste v C++ delat nechci, protoze to je hruza. To si to radeji napisu v C a budu delat polymorfismus pres pointry na funkce.
Ale v C to přece vyjde na stejno. Pokud budu dělat polymorfismus Cčkovským stylem, tzn bázová struktura bude první položka té odvozené, tak jsou při kopírování té struktury úplně stejné problémy. Jenom to přetypování ukazatele z odvozené struktury na bázovou se musí napsat ručně. Pokud tu strukturu alokuju na haldě, tak je to stejně bezproblémové v C i C++. A pokud ji vytvořím na stacku, tak mi kód, který nic neví o odvozené struktuře, vyzobne a "zbastardizuje" tu strukturu i v čistém C. Zrovna tady přidává C++ jenom syntaktický cukr a principy z Cčka zůstávají.
-
ja jsem to uplne 100% nepochopil, ale doptavat uz se nebudu, tohle co pisete to uz je proste fakt moc. Ja nikdy proste v C++ delat nechci, protoze to je hruza. To si to radeji napisu v C a budu delat polymorfismus pres pointry na funkce.
Ale v C to přece vyjde na stejno. Pokud budu dělat polymorfismus Cčkovským stylem, tzn bázová struktura bude první položka té odvozené, tak jsou při kopírování té struktury úplně stejné problémy. Jenom to přetypování ukazatele z odvozené struktury na bázovou se musí napsat ručně. Pokud tu strukturu alokuju na haldě, tak je to stejně bezproblémové v C i C++. A pokud ji vytvořím na stacku, tak mi kód, který nic neví o odvozené struktuře, vyzobne a "zbastardizuje" tu strukturu i v čistém C. Zrovna tady přidává C++ jenom syntaktický cukr a principy z Cčka zůstávají.
To jsem ted uplne asi nepochopil, v C bych mel design takovy, ze polymorfismus budu delat jen nad funkcemi, nikoliv nad strukturami. Nad strukturami navic asi uplne polymorfismus delat nepotrebuju.
Mimochodem jde v C udelat to, ze 1 hlavicka bude pro 2 ruzne implementace? Ze by pak hlavicka hrala roli interfacu (neplest prosim s OOP) k vicero implementacim?
-
Ruční volání destruktorů nebo třeba zavírání souborů by mě už nebavilo. Proč bych to měl hlídat?
V cpp zadne rucni volani destruktoru neni potreba. Akorat lze presne zjistit kouknutim do kodu, kdy se tak stane.
Takže se zánikem posledního deskriptoru na objekt se automaticky zavolá jeho destruktor?
Tohle je definice destruktoru, nedeterministický úklid dělá finalizér. Některé jazyky mají obojí.
-
To jsem ted uplne asi nepochopil, v C bych mel design takovy, ze polymorfismus budu delat jen nad funkcemi, nikoliv nad strukturami. Nad strukturami navic asi uplne polymorfismus delat nepotrebuju.
Funkce k sobě obvykle potřebujou nějaké specifické data. Situace, že by všem polymorfním funkcím stačila stejná data mi přijde poměrně vzácná. Všechny rozumné callbacky, které berou nějaký pointer na funkci berou i nějaký void pointer na data. Cpát jim je v globálách sice jde, ale není to ono. Jinak C++ kód
class Base
{
public:
virtual void foo();
int a;
};
class Derived : public Base
{
void foo() override;
int b;
};
odpovídá C kódu :
struct Base
{
struct Vft *vft;
int a;
};
struct Derived
{
struct Base base;
int b;
};
struct Vft
{
void (*foo)( Base *this );
};
+ 2 konstaty typu Vft s ukazateli na dvě verze funkce foo.
C++ k tomuhle dělá v podstatě jen syntaktický cukr, takže samo nastavuje pointery na Vft, automaticky konvertuje typy pointerů a zjednodušuje volání metod. Třeba v Cčkových hlavičkách pro windowsí COM objekty se dá najít, jak vypadá volání virtuálních funkcí C++ z C kódu. Implementace objektů v C vypadá obvykle velice podobně, jen se občas vypouští ta vft a pointery na funkce se cpou přímo do Base, pokud jich není moc. Kód, který dostane pointer na Base pak nemůže vědět, jestli je to Base, nebo začátek Derived. A pokud by tu Base zkopíroval, tak udělá slicing jako C++ (jen s drobným rozdílem, že v C++ se nebude slepě kopírovat ten pointer na vft).
Mimochodem jde v C udelat to, ze 1 hlavicka bude pro 2 ruzne implementace? Ze by pak hlavicka hrala roli interfacu (neplest prosim s OOP) k vicero implementacim?
Samozřejmě. Záleží jenom na tom, jaké obj soubory dostane linker. Nebo jaká verze .dll/.so se předhodí programu při startu. V C se funkce linkují čistě podle jména. Linker dokonce ani nemůže zkontrolovat typy, protože C na rozdíl od C++ nedělá name mangling.
-
Dekuji za priklad. Presto si myslim, ze mi zde neco v C paradigmatu unika. Ono neni vzdycky nutne ani ten polymorfismus pouzivat. Mam treba ted komponentu o 300k radku kodu v Jave, kde polymorfismus a generika se pouziva velice vzacne - v podstate hlavne v testech pri mockovani. A taky to jde a je to prehledne, ne-li prehlednejsi, protoze tam nejsou zaludnosti.
Myslim si, ze k programovani se da pristupovat i jinak, ale jsem zvykly na Javu a OOP paradigma a ono to na to C-paradigma napasovat nejde, ale dost mozna ani to jit nemusi.
Me se moc OOP programovani v C nelibi, ikdyz to jde. Ale profi zkusenost s C nemam. Ze zvyku hned vyhledavam jak udelat polymorfismus, ikdyz to treba vlastne ani neni nutne a da se to vymyslet jinak.
Ono kolikrat jazyk obsahuje nejruznejsi funkcionality, treba C#, a clovek pak muze nabyt klameho dojmu ze bez nich to nejde a dokonce je pouziva zbytecne a spatne, jako napr. psat vsechno async, vsude se snazit napasovat generikum, vsude se snazit strcit yield, vsude pouzivat "?" misto toho aby se clovek zamyslel proc musi porad checkovat neco na null, jestli neni nekde chyba v designu.
C umi vsechno co je k napsani aplikace potreba, mozna jestli neni nekdy chyba mezi klavesnici a zidli :-) Ja vlastne ani nevim, jak se ma v C spravne aplikace psat, pcinaje VS delam jen v OOP jazycich.
-
Jenom tak na okraj: v Go se slicing dělá i s pointery, je to jedna z věcí, které mě na Go fakt štvou. Při volání se prostě předá ukazatel opravdu jenom na ten typ, ktery v hlavičce je (tj. jenom embedded struct Base). Je to sice konzistentnější při jednom použití (volání odkazem i hodnotou se chová stejně), ale zase je nekonzistentní, že když tu samou funkci volám přímo na structu a nebo až uvnitř funkce, kam jsem struct předal, výsledek je jiný (tím voláním funkce se "vyřízne" jenom embedded struct):
https://play.golang.org/p/MCmwySyPQpl
-
Aby volající kód mohl kopírovat neznámý odvozený typ, musel by mít k dispozici něco jako "virtuální kopírovací konstruktor" + podporu objektů neznámého typu na zásobníku.
To by bylo možná lepší řešení - na způsob Copy traitu v Rustu.
Co si mám představit pod "podporu objektů neznámého typu na zásobníku"? Funkce s těmi daty nic nemusí dělat, musí je umět jenom zkopírovat a vědět, kde začínají data, kterým rozumí. Ostatní může v klidu ignorovat, ne?
-
Jenom tak na okraj: v Go se slicing dělá i s pointery, je to jedna z věcí, které mě na Go fakt štvou. Při volání se prostě předá ukazatel opravdu jenom na ten typ, ktery v hlavičce je (tj. jenom embedded struct Base). Je to sice konzistentnější při jednom použití (volání odkazem i hodnotou se chová stejně), ale zase je nekonzistentní, že když tu samou funkci volám přímo na structu a nebo až uvnitř funkce, kam jsem struct předal, výsledek je jiný (tím voláním funkce se "vyřízne" jenom embedded struct):
https://play.golang.org/p/MCmwySyPQpl
To je docela konzistentní chování. Navíc ten kód není zrovna idiomatický. Jedním slovem: non-issue.
-
Navíc ten kód není zrovna idiomatický.
Ten kód jenom ukazuje, co se tam děje. Jinak to ukázat najde (AFAIK), takže tuhle výhradu neberu.
Jedním slovem: non-issue.
Může to být issue pro lidi, kteří mají subtypový polymorfismus pod kůží, chtějí nadesignovat kód tak, jak jsou zvyklí (předám někam Derived jako Base, něco se s tím děje, pak to dostanu zpátky a přetypuju si zase na Derived), a ejhle: ono to v Go takhle nejde :)
Jasně, správný Go řešení je použít interfaces, o tom se nehádám, jenom říkám, že to může být pro někoho docela překvapení. Mně osobně tenhle "supersilnej nominalismus" ("objekt" se chová ne podle toho čím je, ale podle toho, jaký typ je v kódu u něj napsaný) mně osobně moc nevyhovuje.
-
Navíc ten kód není zrovna idiomatický.
Ten kód jenom ukazuje, co se tam děje. Jinak to ukázat najde (AFAIK), takže tuhle výhradu neberu.
Jedním slovem: non-issue.
Může to být issue pro lidi, kteří mají subtypový polymorfismus pod kůží, chtějí nadesignovat kód tak, jak jsou zvyklí (předám někam Derived jako Base, něco se s tím děje, pak to dostanu zpátky a přetypuju si zase na Derived), a ejhle: ono to v Go takhle nejde :)
Jasně, správný Go řešení je použít interfaces, o tom se nehádám, jenom říkám, že to může být pro někoho docela překvapení. Mně osobně tenhle "supersilnej nominalismus" ("objekt" se chová ne podle toho čím je, ale podle toho, jaký typ je v kódu u něj napsaný) mně osobně moc nevyhovuje.
Když takhle napíšu Base, jde o kompozici. Jak jinak by to asi mělo fungovat?
-
Když takhle napíšu Base, jde o kompozici. Jak jinak by to asi mělo fungovat?
Problém je v tom, že se to na první pohled tváří jako subtypový polymorfismus (Derived můžu předat do funkce, která očekává Base), ale ve skutečnosti to tak není. Protože to může běžného frantu programátora zmást, možná by neškodilo víc explicitnosti:
func f(b Base) {
...
}
...
d := Derived{...}
// ok
f(d.Base)
// err
f(d)
- pokud by to bylo takhle, bylo by na první pohled jasné, že do f předávám jenom část d.
...ale nechci autorům Go radit, chtěl jsem jenom říct, že taková nepříjemná zákoutí nejsou jenom v C++, ale bohužel i v Go, které jinak lidi mají za dost čistý a konzistentní jazyk.
-
Když takhle napíšu Base, jde o kompozici. Jak jinak by to asi mělo fungovat?
Problém je v tom, že se to na první pohled tváří jako subtypový polymorfismus (Derived můžu předat do funkce, která očekává Base), ale ve skutečnosti to tak není. Protože to může běžného frantu programátora zmást, možná by neškodilo víc explicitnosti:
func f(b Base) {
...
}
...
d := Derived{...}
// ok
f(d.Base)
// err
f(d)
- pokud by to bylo takhle, bylo by na první pohled jasné, že do f předávám jenom část d.
...ale nechci autorům Go radit, chtěl jsem jenom říct, že taková nepříjemná zákoutí nejsou jenom v C++, ale bohužel i v Go, které jinak lidi mají za dost čistý a konzistentní jazyk.
Go zrovna moc konzistentní není. Ale ta implicitní kompozice dává smysl. Že si někdo myslí, že Go má podtypový polymorfismus, to už je jiná věc, taky nebudu kritizovat Porsche Carrera, že s ním nemůžu orat pole, na základě toho, že jsem si myslel, že to jde.
-
taky nebudu kritizovat Porsche Carrera, že s ním nemůžu orat pole, na základě toho, že jsem si myslel, že to jde.
Janže ono to jde a není to žádná obezlička, je to jasná součást jazyka - Derived můžu předat do funkce, která očekává Base. Nepředávám d.Base, předávám d. Je to nejspíš jediná situace, kde se v Go může předávat jiný typ než očekávaný.
A protože to není intuitivní, vyžaduje to explicitní vysvětlení:
There's an important way in which embedding differs from subclassing. When we embed a type, the methods of that type become methods of the outer type, but when they are invoked the receiver of the method is the inner type, not the outer one. In our example, when the Read method of a bufio.ReadWriter is invoked, it has exactly the same effect as the forwarding method written out above; the receiver is the reader field of the ReadWriter, not the ReadWriter itself.
https://golang.org/doc/effective_go.html#embedding
Není to ani čisté skládání (jako v C), ani subtyping. Je to takové divné "něco mezi" s vlastnostmi, které si člověk musí pamatovat, protože nikde jinde to tak nefunguje (naštěstí jich není moc).
-
taky nebudu kritizovat Porsche Carrera, že s ním nemůžu orat pole, na základě toho, že jsem si myslel, že to jde.
Janže ono to jde a není to žádná obezlička, je to jasná součást jazyka - Derived můžu předat do funkce, která očekává Base. Nepředávám d.Base, předávám d. Je to nejspíš jediná situace, kde se v Go může předávat jiný typ než očekávaný.
A protože to není intuitivní, vyžaduje to explicitní vysvětlení:
There's an important way in which embedding differs from subclassing. When we embed a type, the methods of that type become methods of the outer type, but when they are invoked the receiver of the method is the inner type, not the outer one. In our example, when the Read method of a bufio.ReadWriter is invoked, it has exactly the same effect as the forwarding method written out above; the receiver is the reader field of the ReadWriter, not the ReadWriter itself.
https://golang.org/doc/effective_go.html#embedding
Není to ani čisté skládání (jako v C), ani subtyping. Je to takové divné "něco mezi" s vlastnostmi, které si člověk musí pamatovat, protože nikde jinde to tak nefunguje (naštěstí jich není moc).
Jo, je nutné si to pamatovat. Toho je v Go víc. Pak je otázka, jestli se taky člověk bez takovéhoto embeddingu obejde.
-
Pak je otázka, jestli se taky člověk bez takovéhoto embeddingu obejde.
Jasně, to je legitimní otázka. A odpověď jsem už naťukl: v C se bez něj obejdeš, protože Base je skutečně explicitně embeddovaný - je to prostě field v Derived. Čili podle mě je to v Go čistě syntaktický cukr, kdy místo d.Base napíšeš d a překladač to pochopí. Z toho vyvozuju, že všechno by mohlo zůstat jak je, jenom bys musel psát d.Base. Neumím si představit cokoli, o co by tím jazyk přišel.
-
Co si mám představit pod "podporu objektů neznámého typu na zásobníku"? Funkce s těmi daty nic nemusí dělat, musí je umět jenom zkopírovat a vědět, kde začínají data, kterým rozumí. Ostatní může v klidu ignorovat, ne?
Měl jsem na mysli hlavně velikost známou až v runtimu. Jedna věc jsou dočasné proměnné. Ty jsou v pohodě, i když najednou nutně potřebujeme registr pro frame pointer. Druhá věc je ale vracení objektu neznámé velikosti hodnotou. Volající neví kolik paměti vyhradit a volaný to taky nezvládne alokovat bez větších problémů. Musel by ten zásobník přeskládat. Neznám žádné volací konvence, které by něco takového uměly.
-
Měl jsem na mysli hlavně velikost známou až v runtimu. Jedna věc jsou dočasné proměnné. Ty jsou v pohodě, i když najednou nutně potřebujeme registr pro frame pointer. Druhá věc je ale vracení objektu neznámé velikosti hodnotou. Volající neví kolik paměti vyhradit a volaný to taky nezvládne alokovat bez větších problémů. Musel by ten zásobník přeskládat. Neznám žádné volací konvence, které by něco takového uměly.
Já ty lowlevel detaily dobře neznám a C++ jsem viděl naposledy tak před deseti lety, takže mi asi spousta věcí uniká, ale čistě od stolu mě překvapuje, že je to problém. Přijde mi, že by stačily dvě čísla na začátku: velikost Derived a offset Base v rámci Derived. Pokud potřebuju Derived někam zkopírovat, vím jeho velikost a víc nepotřebuju. Pokud potřebuju šahat do Base, vím jeho offset, což je taky všechno, co potřebuju.
-
Měl jsem na mysli hlavně velikost známou až v runtimu. Jedna věc jsou dočasné proměnné. Ty jsou v pohodě, i když najednou nutně potřebujeme registr pro frame pointer. Druhá věc je ale vracení objektu neznámé velikosti hodnotou. Volající neví kolik paměti vyhradit a volaný to taky nezvládne alokovat bez větších problémů. Musel by ten zásobník přeskládat. Neznám žádné volací konvence, které by něco takového uměly.
Já ty lowlevel detaily dobře neznám a C++ jsem viděl naposledy tak před deseti lety, takže mi asi spousta věcí uniká, ale čistě od stolu mě překvapuje, že je to problém. Přijde mi, že by stačily dvě čísla na začátku: velikost Derived a offset Base v rámci Derived. Pokud potřebuju Derived někam zkopírovat, vím jeho velikost a víc nepotřebuju. Pokud potřebuju šahat do Base, vím jeho offset, což je taky všechno, co potřebuju.
Uložit někam velikost není problém. Ten je v tom, že na zásobník se ukládají i návratové adresy a parametry při volání funkcí. Takže volající na zásobník nacpe parametry a pak se tam uloží i návratová adresa. Pokud volající zná přesnou velikost vraceného objektu, tak si může vyhradit lokální proměnnou a předat její adresu jako parametr. Pokud tu velikost nezná, tak má smůlu.
Volaný si může na zásobník nacpat co chce, jenže před koncem musí všechno zase uklidit aby na vrchu byla návratová adresa. Jinak by se nešlo vrátit. Mohl by pošoupnout parametry a návratovou hodnotu a tím vyhradit místo. Tohle je komplikace nejen na straně volaného, ale i volajícího, kterému by se pod rukama změnil stack pointer.
Běžná a poměrně důležitá optimalizace je vypuštění frame pointeru. Tím se ušetří jeden registr a zjednoduší prolog a epilog funkce. Pokud nejde v compile-time přesně modelovat věci na zásobníku, tak tuhle optimalizaci nemůžeme udělat. Variable length arrays nebo alloca v C tuhle optimalizaci znemožní v rámci jedné funkce. Tohle by prosáklo i ven. A navíc takovéhle skoky neznámé velikosti ztěžují kompilátoru adresování ostatních proměnných na zásobníku.
-
Měl jsem na mysli hlavně velikost známou až v runtimu. Jedna věc jsou dočasné proměnné. Ty jsou v pohodě, i když najednou nutně potřebujeme registr pro frame pointer. Druhá věc je ale vracení objektu neznámé velikosti hodnotou. Volající neví kolik paměti vyhradit a volaný to taky nezvládne alokovat bez větších problémů. Musel by ten zásobník přeskládat. Neznám žádné volací konvence, které by něco takového uměly.
Já ty lowlevel detaily dobře neznám a C++ jsem viděl naposledy tak před deseti lety, takže mi asi spousta věcí uniká, ale čistě od stolu mě překvapuje, že je to problém. Přijde mi, že by stačily dvě čísla na začátku: velikost Derived a offset Base v rámci Derived. Pokud potřebuju Derived někam zkopírovat, vím jeho velikost a víc nepotřebuju. Pokud potřebuju šahat do Base, vím jeho offset, což je taky všechno, co potřebuju.
Polymorfismus na zásobníku principiálně řešit nejde. Proto je jediný obecně použitelný způsob dělat to, co dělá Go a některé jiné překladače/runtimy: escape analýza a na zásobniku jen to, co nezpůsobí problém. Ono, upřímně, alokací na haldě, které tam skutečně být musí, bývá typicky hodně málo a alokátory jsou natolik efektivní (včetně tracing GC), že autorům překladačů/runtimů nestojí za to se nějakým zásobníkem zabývat. Občas někdo uprasí nějaký šílený hack (nový runtime pro ObjC má intrinziky kolem správy paměti až na úroveň asembleru), ale v 99% případů se to nevyplatí.
-
Ok, už asi trochu tuším, o co jde :) Dík.
-
V první řadě je otázka, co to znamená ideální, nejspíš nejde udělat jazyk, který by se hodil na všechno. Ale když vezmu mainstream, tak je rozhodně nejdůležitější, aby byl jednoduchý, asi tak na úrovni javy (i ta má věci navíc, jako je třeba vestavěná synchronizace).
Syntaxe by se měla držet osvědčených zvyků ohledně operátorů, závorek atd., některé nové jazyky to zbytečně prohazují. Dál základní OOP, funkcionální prvky, aby stačilo říct "co" místo "jak". Takže streamy (ale jednodušší než v javě), lambdy, konstantní objekty...
Rozhodně musí mít GC, ruční správa paměti je ve většině případů špatná věc. Stejně tak by se programátor neměl starat o to, zda budou data na stacku nebo heapu, tohle zvládne překladač či runtime dost dobře sám. C++11 jasně ukazuje, že je špatný nápad tím prasit jazyk a že z toho cesta nevede. Smart pointery, move semantika atd. to ve výsledku ještě zhorší.
A bylo by hezké, kdyby měl nějak snadno zvládnuto ORM, JSON, HTTP, prostě napojení na běžné okolní prostředí.
-
Syntaxe by se měla držet osvědčených zvyků ohledně operátorů, závorek atd., některé nové jazyky to zbytečně prohazují. Dál základní OOP, funkcionální prvky, aby stačilo říct "co" místo "jak". Takže streamy (ale jednodušší než v javě), lambdy, konstantní objekty...
Rozhodně musí mít GC, ruční správa paměti je ve většině případů špatná věc. Stejně tak by se programátor neměl starat o to, zda budou data na stacku nebo heapu, tohle zvládne překladač či runtime dost dobře sám. C++11 jasně ukazuje, že je špatný nápad tím prasit jazyk a že z toho cesta nevede. Smart pointery, move semantika atd. to ve výsledku ještě zhorší.
A bylo by hezké, kdyby měl nějak snadno zvládnuto ORM, JSON, HTTP, prostě napojení na běžné okolní prostředí.
Skoro přesně jsi popsal Go :)
-
Rozhodně musí mít GC, ruční správa paměti je ve většině případů špatná věc.
Fakt?
-
Syntaxe by se měla držet osvědčených zvyků ohledně operátorů, závorek atd., některé nové jazyky to zbytečně prohazují. Dál základní OOP, funkcionální prvky, aby stačilo říct "co" místo "jak". Takže streamy (ale jednodušší než v javě), lambdy, konstantní objekty...
Rozhodně musí mít GC, ruční správa paměti je ve většině případů špatná věc. Stejně tak by se programátor neměl starat o to, zda budou data na stacku nebo heapu, tohle zvládne překladač či runtime dost dobře sám. C++11 jasně ukazuje, že je špatný nápad tím prasit jazyk a že z toho cesta nevede. Smart pointery, move semantika atd. to ve výsledku ještě zhorší.
A bylo by hezké, kdyby měl nějak snadno zvládnuto ORM, JSON, HTTP, prostě napojení na běžné okolní prostředí.
Skoro přesně jsi popsal Go :)
;D
-
Rozhodně musí mít GC, ruční správa paměti je ve většině případů špatná věc.
Fakt?
Ano. U jednoduchých algoritmů to moc nevadí, ale jakmile je to složitější, tak v konečném důsledku ten GC programátor píše pokaždé znova. Je to v tom algoritmu skryto, zbytečně ho to zesložiťuje a v kombinaci s případnými smartptr to nakonec může být i pomalejší.
Čili otázka není zda GC ano či ne, ale zda má být jednou v runtime nebo ho má psát programátor.
-
Syntaxe by se měla držet osvědčených zvyků ohledně operátorů, závorek atd., některé nové jazyky to zbytečně prohazují. Dál základní OOP, funkcionální prvky, aby stačilo říct "co" místo "jak". Takže streamy (ale jednodušší než v javě), lambdy, konstantní objekty...
Rozhodně musí mít GC, ruční správa paměti je ve většině případů špatná věc. Stejně tak by se programátor neměl starat o to, zda budou data na stacku nebo heapu, tohle zvládne překladač či runtime dost dobře sám. C++11 jasně ukazuje, že je špatný nápad tím prasit jazyk a že z toho cesta nevede. Smart pointery, move semantika atd. to ve výsledku ještě zhorší.
A bylo by hezké, kdyby měl nějak snadno zvládnuto ORM, JSON, HTTP, prostě napojení na běžné okolní prostředí.
Skoro přesně jsi popsal Go :)
Na to jsem myslel, proto jsem měl napsat, že jediná správná syntax je prefixová, čili
int i = 0
Z dokumentace zrovna vidim
var i, j int = 1, 2
vždyť to je strašný.
Dále už bylo mnoho napsáno o řešení výjimečných stavů v go...
-
Rozhodně musí mít GC, ruční správa paměti je ve většině případů špatná věc.
Fakt?
Ano. U jednoduchých algoritmů to moc nevadí, ale jakmile je to složitější, tak v konečném důsledku ten GC programátor píše pokaždé znova. Je to v tom algoritmu skryto, zbytečně ho to zesložiťuje a v kombinaci s případnými smartptr to nakonec může být i pomalejší.
Čili otázka není zda GC ano či ne, ale zda má být jednou v runtime nebo ho má psát programátor.
GC (tracing) dává smysl ve spojení s kvalitní escape analýzou, pokud všechno, kde to je technicky možné, alokuju na zásobníku, nemusím toho moc na haldě alokovat a běh GC se pak znatelně neprojeví. Nicméně správa paměti v C++ je taky dost dobře promyšlená.
-
C++11 jasně ukazuje, že je špatný nápad tím prasit jazyk a že z toho cesta nevede. Smart pointery, move semantika atd. to ve výsledku ještě zhorší.
Naopak C++ je ve správě paměti od jedenáctky super. Člověk nad tím má kontrolu a přesto se o moc nemusí starat. Opravdu se mi dlouho v C++ nestalo, že bych měl problém s pamětí a zároveň nemám pocit, že by to kód nějak prasilo.
Ano. U jednoduchých algoritmů to moc nevadí, ale jakmile je to složitější, tak v konečném důsledku ten GC programátor píše pokaždé znova.
Tak bavíme-li se o algoritmu, tak předpokládám, že pracuji s nějakými "strukturami", které se alokují/dealokují samy. Od toho máme konstruktory a destruktory. Zrovna s algoritmy mám v C++ problém z jiných důvodů, než je správa paměti.
-
V první řadě je otázka, co to znamená ideální, nejspíš nejde udělat jazyk, který by se hodil na všechno. Ale když vezmu mainstream, tak je rozhodně nejdůležitější, aby byl jednoduchý, asi tak na úrovni javy (i ta má věci navíc, jako je třeba vestavěná synchronizace).
Syntaxe by se měla držet osvědčených zvyků ohledně operátorů, závorek atd., některé nové jazyky to zbytečně prohazují. Dál základní OOP, funkcionální prvky, aby stačilo říct "co" místo "jak". Takže streamy (ale jednodušší než v javě), lambdy, konstantní objekty...
Rozhodně musí mít GC, ruční správa paměti je ve většině případů špatná věc. Stejně tak by se programátor neměl starat o to, zda budou data na stacku nebo heapu, tohle zvládne překladač či runtime dost dobře sám. C++11 jasně ukazuje, že je špatný nápad tím prasit jazyk a že z toho cesta nevede. Smart pointery, move semantika atd. to ve výsledku ještě zhorší.
A bylo by hezké, kdyby měl nějak snadno zvládnuto ORM, JSON, HTTP, prostě napojení na běžné okolní prostředí.
No lol... tak to urcite.. to je pekna snuska...
Naopak od C++11 se o to neni potreba starat a funguje to. Narozdil od GC jazyku jako java navic i deterministicky, takze neni potreba jakekoli komplikovanejsi veci ojebavat pres manualni spravu pameti. Jak je videt na vetsich komplikovanejsich java projektech.
Jn, problemy, na ktere lze pouzivat ORM.. na to c++ potreba neni, to je pravda.. tam bohate staci python.
-
Naopak C++ je ve správě paměti od jedenáctky super. Člověk nad tím má kontrolu a přesto se o moc nemusí starat.
Do jedenáctky byly dva způsoby jak objekt vytvořit a o něco více způsobů jak ho předat parametrem do funkce. Od C++11 je těch způsobů založení 4 a předání nesčetně více. To vše jsou věci, kde musí programátor dělat rozhodnutí, kde se to musí předělávat při refactoringu, kde se to musí nějak řešit při předávání do knihoven apod. Rozhodně bych tak komplexní věc nenazýval "...moc nemusí starat".
Tak bavíme-li se o algoritmu, tak předpokládám, že pracuji s nějakými "strukturami", které se alokují/dealokují samy. Od toho máme konstruktory a destruktory. Zrovna s algoritmy mám v C++ problém z jiných důvodů, než je správa paměti.
V C++ se nepracuje se samotnými strukturami, ale s jejich hodnotami, odkazy, pointery (několika druhů) apod. To sebou nese spoustu rozhodnutí, které v jiných jazycích nejsou vůbec potřeba, nemají (většinou) žádný vztah k řešenému problému a jsou vynuceny jazykem. Bohužel musím dodat, že to co se kde použije je u většiny programátorů čirý cargo cult.
-
GC (tracing) dává smysl ve spojení s kvalitní escape analýzou, pokud všechno, kde to je technicky možné, alokuju na zásobníku, nemusím toho moc na haldě alokovat a běh GC se pak znatelně neprojeví. Nicméně správa paměti v C++ je taky dost dobře promyšlená.
Když už zmiňuješ GC optimalizace, tak je dobré myslet na to, že new/delete má také nějakou režii a ta má také dopad na výkon. Snaživý C++ programátor o takové režii ví, takže se pustí do věcí jako object pooly apod. čímž si svůj kód dodatečně zaplevelí, algoritmus zesložití a přitom se může lehce stát, že optimalizovaný GC dá mnohem lepší výsledky.
-
Naopak od C++11 se o to neni potreba starat a funguje to.
Na jednoduchých algoritmech možná ano, ale jak to začne být složitější, tak se ukáže, že shared_ptr mají netriviální režii, takže se to přepíše na kombinaci unique a raw pointerů, s tím související redesign aby byla pořešena životnost objektů atd. Prostě není pravda, že není potřeba se starat.
-
Naopak C++ je ve správě paměti od jedenáctky super. Člověk nad tím má kontrolu a přesto se o moc nemusí starat.
Do jedenáctky byly dva způsoby jak objekt vytvořit a o něco více způsobů jak ho předat parametrem do funkce. Od C++11 je těch způsobů založení 4 a předání nesčetně více. To vše jsou věci, kde musí programátor dělat rozhodnutí, kde se to musí předělávat při refactoringu, kde se to musí nějak řešit při předávání do knihoven apod. Rozhodně bych tak komplexní věc nenazýval "...moc nemusí starat".
Ano, ale zdaleka ne všechny musí člověk používat. O tom je C++, neplatím za to, co nepoužívám. A nejkrásnější je na tom, že nikdo nikoho nenutí všechno používat. Takže opravdu se o to programátor starat nemusí.
Tak bavíme-li se o algoritmu, tak předpokládám, že pracuji s nějakými "strukturami", které se alokují/dealokují samy. Od toho máme konstruktory a destruktory. Zrovna s algoritmy mám v C++ problém z jiných důvodů, než je správa paměti.
V C++ se nepracuje se samotnými strukturami, ale s jejich hodnotami, odkazy, pointery (několika druhů) apod.
Proč by se nemělo pracovat se samotnými strukturami? Co je "hodnota třídy"?
To sebou nese spoustu rozhodnutí, které v jiných jazycích nejsou vůbec potřeba, nemají (většinou) žádný vztah k řešenému problému a jsou vynuceny jazykem. Bohužel musím dodat, že to co se kde použije je u většiny programátorů čirý cargo cult.
V jiných jazycích nejsou potřeba, ale jiné jazyky většinou neposkytují takový výkon. :) Ony to ty "jiné jazyky" většinou dělají, ale jaksi "pod kapotou", tudíž když to neudělají dobře, tak to nemám jak ovlivnit.
Naopak od C++11 se o to neni potreba starat a funguje to.
Na jednoduchých algoritmech možná ano, ale jak to začne být složitější, tak se ukáže, že shared_ptr mají netriviální režii, takže se to přepíše na kombinaci unique a raw pointerů, s tím související redesign aby byla pořešena životnost objektů atd. Prostě není pravda, že není potřeba se starat.
Tak shared_ptr většinou není moc proč používat. Pokud někdo používá shared_ptr, aby nemusel řešit životnost objektů, tak C++ fakt není pro něj. To je jako kupovat si Ferrari s tím, že s ním budu jezdit po poli... :)
-
Naopak od C++11 se o to neni potreba starat a funguje to. Narozdil od GC jazyku jako java navic i deterministicky
Ještě k té deterministické dealokaci - dobře že to zmiňuješ, protože to názorně ukazuje posunuté myšlení C++ programátorů. Deterministická dealokace je sama o sobě zbytečná, řešený problém si ji sám o sobě nežádá. Ovšem v C++ je to způsob, jak uvolnit zdroje, takže se tak používá na vše. Jenže už se nemyslí na to, že to má neblahý vliv na výkon dealokace. Ta kdyby se nedělala pro každý objekt zvlášť, ale pro větší bloky, tak je to mnohem výhodnější a právě to je moment, ve kterém získává GC navrch - uklízí až když je to nutné, což je v běžných scénářích mnohem výhodnější než se starat o každý mazaný objekt.
-
...a právě to je moment, ve kterém získává GC navrch - uklízí až když je to nutné, což je v běžných scénářích mnohem výhodnější než se starat o každý mazaný objekt.
Čistě z logiky věci, takhle fungující GC by asi nebyl moc výkonný... nutné je to, když paměť chybí, a tudíž buď a) nemáme dost paměti a pak je úplně jedno, jestli jedu RAII, nebo GC, nebo ruční správu b) paměť už měla být uvolněná.
-
Naopak od C++11 se o to neni potreba starat a funguje to. Narozdil od GC jazyku jako java navic i deterministicky
Ještě k té deterministické dealokaci - dobře že to zmiňuješ, protože to názorně ukazuje posunuté myšlení C++ programátorů. Deterministická dealokace je sama o sobě zbytečná, řešený problém si ji sám o sobě nežádá. Ovšem v C++ je to způsob, jak uvolnit zdroje, takže se tak používá na vše. Jenže už se nemyslí na to, že to má neblahý vliv na výkon dealokace. Ta kdyby se nedělala pro každý objekt zvlášť, ale pro větší bloky, tak je to mnohem výhodnější a právě to je moment, ve kterém získává GC navrch - uklízí až když je to nutné, což je v běžných scénářích mnohem výhodnější než se starat o každý mazaný objekt.
Naopak, to je nejvetsi pain GC a to je proc pri serioznim pouziti jsou s Javou obrovske problemy. Nikdy nevis kdy zacne GC uvolnovat a jak velkou kupu pameti. Vede to casto k tomu, ze jakekoli vetsi aplikace se v jave ladi daleko pracneji nez i ve starem C.
-
Naopak od C++11 se o to neni potreba starat a funguje to. Narozdil od GC jazyku jako java navic i deterministicky
Ještě k té deterministické dealokaci - dobře že to zmiňuješ, protože to názorně ukazuje posunuté myšlení C++ programátorů. Deterministická dealokace je sama o sobě zbytečná, řešený problém si ji sám o sobě nežádá. Ovšem v C++ je to způsob, jak uvolnit zdroje, takže se tak používá na vše. Jenže už se nemyslí na to, že to má neblahý vliv na výkon dealokace. Ta kdyby se nedělala pro každý objekt zvlášť, ale pro větší bloky, tak je to mnohem výhodnější a právě to je moment, ve kterém získává GC navrch - uklízí až když je to nutné, což je v běžných scénářích mnohem výhodnější než se starat o každý mazaný objekt.
Naopak, to je nejvetsi pain GC a to je proc pri serioznim pouziti jsou s Javou obrovske problemy. Nikdy nevis kdy zacne GC uvolnovat a jak velkou kupu pameti. Vede to casto k tomu, ze jakekoli vetsi aplikace se v jave ladi daleko pracneji nez i ve starem C.
Kde jsi v Jave proboha potreboval ladit GC? :D
-
Ano, ale zdaleka ne všechny musí člověk používat. O tom je C++, neplatím za to, co nepoužívám. A nejkrásnější je na tom, že nikdo nikoho nenutí všechno používat. Takže opravdu se o to programátor starat nemusí.
To co označuješ za výhodu (že se nemusí starat), lze z druhé strany nahlédnout jako nevýhodu (když se nepostará, tak nedostane). Stejně tak platí výkonem za to, že nepoužil co mohl.
Je to hezká teorie, ale v praxi spíš vidím, že C++ programátoři jsou řešením paměti úplně posedlí.
Proč by se nemělo pracovat se samotnými strukturami? Co je "hodnota třídy"?
Tím jsem myslel strukturu na stacku, čili že se chová jako hodnota.
V jiných jazycích nejsou potřeba, ale jiné jazyky většinou neposkytují takový výkon. :) Ony to ty "jiné jazyky" většinou dělají, ale jaksi "pod kapotou", tudíž když to neudělají dobře, tak to nemám jak ovlivnit.
To je pointa celé mé úvahy, že ten výkon můžu klidně dostat tak, že to napíšu v jazyku, ve kterém nemusím řešit polovinu věcí, protože GC, to mi výrazně zjednoduší kód a ve výsledku to JIT zoptimalizuje líp, než bych to napsal v tom C++.
Tak shared_ptr většinou není moc proč používat. Pokud někdo používá shared_ptr, aby nemusel řešit životnost objektů, tak C++ fakt není pro něj. To je jako kupovat si Ferrari s tím, že s ním budu jezdit po poli... :)
Teď mi to budeš muset lépe vysvětlit. Na jednu stranu zde čtu, že C++11 výrazně zlepšuje práci s pamětí (a snad chápu jak je to myšleno) a vzápětí říkáš, že shared_ptr není na životnost objektů. K čemu tedy je?
-
Naopak, to je nejvetsi pain GC a to je proc pri serioznim pouziti jsou s Javou obrovske problemy. Nikdy nevis kdy zacne GC uvolnovat a jak velkou kupu pameti. Vede to casto k tomu, ze jakekoli vetsi aplikace se v jave ladi daleko pracneji nez i ve starem C.
Popravdě jsem nikdy nedělal v Javě nic tak velkého. Zatím mi vždy stačil přístup napsat to jednoduše, hlavně pustit z hlavy poučky o mikro optimalizacích z C++ a v případě problému to zprofilovat.
Docela mě zajímá, jaké charakteristiky má taková větší aplikace u které dělá problém spuštění GC.
-
Ano, ale zdaleka ne všechny musí člověk používat. O tom je C++, neplatím za to, co nepoužívám. A nejkrásnější je na tom, že nikdo nikoho nenutí všechno používat. Takže opravdu se o to programátor starat nemusí.
To co označuješ za výhodu (že se nemusí starat), lze z druhé strany nahlédnout jako nevýhodu (když se nepostará, tak nedostane). Stejně tak platí výkonem za to, že nepoužil co mohl.
Je to hezká teorie, ale v praxi spíš vidím, že C++ programátoři jsou řešením paměti úplně posedlí.
Ano, může to být i nevýhoda. Ale když se nepostará, tak jsme +/- u céčka. Pokud nepoužil, co mohl, tak mu asi nejde primárně o výkon, a pak tedy záleží, co vlastně programátor chce... :)
Na tom posledním se shodneme.
Proč by se nemělo pracovat se samotnými strukturami? Co je "hodnota třídy"?
Tím jsem myslel strukturu na stacku, čili že se chová jako hodnota.
Tak na stacku se ta paměť moc "manuálně" řešit nemusí, takže spíš nechápu souvislost.
V jiných jazycích nejsou potřeba, ale jiné jazyky většinou neposkytují takový výkon. :) Ony to ty "jiné jazyky" většinou dělají, ale jaksi "pod kapotou", tudíž když to neudělají dobře, tak to nemám jak ovlivnit.
To je pointa celé mé úvahy, že ten výkon můžu klidně dostat tak, že to napíšu v jazyku, ve kterém nemusím řešit polovinu věcí, protože GC, to mi výrazně zjednoduší kód a ve výsledku to JIT zoptimalizuje líp, než bych to napsal v tom C++.
Ano, i ne. Záleží, jak moc se chce člověk spoléhat na GC. Problém GC bývá i to, že prostě není schopen nejlepšího řešení (už z principu věci). Někdy je problém latence, jindy spotřeba paměti... s RAII i ruční správou toho nejlepšího řešení dosáhnout mohu, pokud to bude potřeba.
Tak shared_ptr většinou není moc proč používat. Pokud někdo používá shared_ptr, aby nemusel řešit životnost objektů, tak C++ fakt není pro něj. To je jako kupovat si Ferrari s tím, že s ním budu jezdit po poli... :)
Teď mi to budeš muset lépe vysvětlit. Na jednu stranu zde čtu, že C++11 výrazně zlepšuje práci s pamětí (a snad chápu jak je to myšleno) a vzápětí říkáš, že shared_ptr není na životnost objektů. K čemu tedy je?
Tak nějak doufám, že je to pozdní hodinou a ne tím, že by sis myslel, že
"Pokud někdo používá shared_ptr, aby nemusel řešit životnost objektů..."
je stejné jako
shared_ptr není na životnost objektů
.
Problém je, že pokud někdo používá shared_ptr místo unique_ptr, resp. používá shared_ptr tak, že nechce řešit, kde co alokoval, jaká je scope a že by tedy občas měl udělat nějaký move, tak není C++ pro něj. To se to pak může fakt psát v té Javě.
Jen podotýkám, že nejsem žádný expert na C++, ale moc si nevzpomínám, kdy bych byl nucen použít shared_ptr.
-
Problém je, že pokud někdo používá shared_ptr místo unique_ptr, resp. používá shared_ptr tak, že nechce řešit, kde co alokoval, jaká je scope a že by tedy občas měl udělat nějaký move, tak není C++ pro něj. To se to pak může fakt psát v té Javě.
Jen podotýkám, že nejsem žádný expert na C++, ale moc si nevzpomínám, kdy bych byl nucen použít shared_ptr.
Nemyslím to v situacích, kde by šel shared_ptr nahradit pomocí unique nebo move apod. Já se s tím běžně setkávám u grafových algoritmů, kde prostě není jeden vlastník objektu, ale je jich víc rovnocenných. V takovém případě buď použiju shared a daný objekt zmizí s posledním vlastníkem nebo bych si to musel evidovat někde bokem.
-
Myslim, ze to je v Rustu, i kdyz jsem o rom cetl jen letmo, ze = pro objekty je automaticky move. To se mi libi oproti c++.
-
Problém je, že pokud někdo používá shared_ptr místo unique_ptr, resp. používá shared_ptr tak, že nechce řešit, kde co alokoval, jaká je scope a že by tedy občas měl udělat nějaký move, tak není C++ pro něj. To se to pak může fakt psát v té Javě.
Jen podotýkám, že nejsem žádný expert na C++, ale moc si nevzpomínám, kdy bych byl nucen použít shared_ptr.
Nemyslím to v situacích, kde by šel shared_ptr nahradit pomocí unique nebo move apod. Já se s tím běžně setkávám u grafových algoritmů, kde prostě není jeden vlastník objektu, ale je jich víc rovnocenných. V takovém případě buď použiju shared a daný objekt zmizí s posledním vlastníkem nebo bych si to musel evidovat někde bokem.
Tak je otázkou, jestli by se měly vrcholy vlastnit mezi sebou... U stromů to není problém, ale tam není problém použít unique_ptr. U jiných grafů si dovedu představit, že si je všechny držím v nějaké struktuře a mezi sebou mají jen pointery, nebo seznam id ostatních vrcholů. Se shared_ptr by mohl být problém např. u cyklů, když nad tím přemýšlím...
-
Proti cyklum s shared_ptr slouzi weak ptr.
-
Myslim, ze to je v Rustu, i kdyz jsem o rom cetl jen letmo, ze = pro objekty je automaticky move. To se mi libi oproti c++.
Jo, ale jenom pokud pro typ není definován trait Copy.
Viz https://doc.rust-lang.org/std/marker/trait.Copy.html a https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html - hlavně část "Ways Variables and Data Interact: Move" a následující.
-
Na to jsem myslel, proto jsem měl napsat, že jediná správná syntax je prefixová, čili
int i = 0
Z dokumentace zrovna vidim
var i, j int = 1, 2
vždyť to je strašný.
Vůbec to není strašný, je to jenom ze začátku nezvyk, který ale člověk rychle překoná. Rozhodně je to nekonečně lepší syntax než céčkový "zápis do spirály" (šnekfixový zápis? ;) ), což je naprostá zhůvěřilost, která nikdy neměla vzniknout.
Dále už bylo mnoho napsáno o řešení výjimečných stavů v go...
Go má spoustu ujetostí a v praktickém používání mě hodně zklamalo, ale to, co jsi psal, prostě celkem přesně sedí :)
-
ale to, co jsi psal, prostě celkem přesně sedí :)
P.S. já osobně bych od moderního jazyka chtěl spíš věci jako:
1. má dobře vyřešenou správu balíčků a jejich závislostí
2. má aspoň nějakou formu generik nebo jiný mocný mechanismus psaní abstraktního kódu
3. má homoikonická makra (ale v dokumentaci je palcovým písmem napsáno, že se nemají zneužívat tam, kde opravdu nejsou potřeba)
4. má pattern matching
5. překladač se snaží seč může odhalit chyby při překladu (a jazyk mu v tom jde naproti)
6. vůbec nemá null, místo něj je Option
7. má alespoň jednoduché algebraické typy
...což jsou přesně painpointy Gočka...
Příklad pokročílé vlastnosti ad 1: Elm umí sám detekovat změnu API a vynutí povýšení sémantické verze při pushnutí balíčku.
ad 5: například relativně jednoduchá, ale strašně užitečná featura překladače, je kontrola vyčerpávajícnosti pattern matchingu (např. když matchuju enum, musím pokrýt všechny zadefinované varianty).
ad 6: Option "navíc", jako možnost, což u některých jazyků je, je k ničemu - pokud je null "občas někde", musím ho čekat všude a tímpádem Option ztrácí význam.
ad 7: stačí jednoduché sum a product types (sorry, nevím, jak se "product type" překládá do češtiny). Praktické hlavně pro jednoduché použití silně typovaných tuples. Praktičnost vylítne do nebes ve spojení s pattern matchingem.
...takže mi do toho sedí spíš Rust než Go ;)
-
3. má homoikonická makra (ale v dokumentaci je palcovým písmem napsáno, že se nemají zneužívat tam, kde opravdu nejsou potřeba)
Teď to neber špatně, ale když jsme tu diskutovali o JavaScriptu, tak standard ani dokumentace nebyly směrodatné, tak mě překvapuje, že máš potřebu to psát do dokumentace. :)
ad 6: Option "navíc", jako možnost, což u některých jazyků je, je k ničemu - pokud je null "občas někde", musím ho čekat všude a tímpádem Option ztrácí význam.
Souhlasím. Co takové std::optional v C++ ? To je taky "option navíc", nebo je to trošku dál?
-
ale to, co jsi psal, prostě celkem přesně sedí :)
P.S. já osobně bych od moderního jazyka chtěl spíš věci jako:
1. má dobře vyřešenou správu balíčků a jejich závislostí
2. má aspoň nějakou formu generik nebo jiný mocný mechanismus psaní abstraktního kódu
3. má homoikonická makra (ale v dokumentaci je palcovým písmem napsáno, že se nemají zneužívat tam, kde opravdu nejsou potřeba)
4. má pattern matching
5. překladač se snaží seč může odhalit chyby při překladu (a jazyk mu v tom jde naproti)
6. vůbec nemá null, místo něj je Option
7. má alespoň jednoduché algebraické typy
...což jsou přesně painpointy Gočka...
Příklad pokročílé vlastnosti ad 1: Elm umí sám detekovat změnu API a vynutí povýšení sémantické verze při pushnutí balíčku.
ad 5: například relativně jednoduchá, ale strašně užitečná featura překladače, je kontrola vyčerpávajícnosti pattern matchingu (např. když matchuju enum, musím pokrýt všechny zadefinované varianty).
ad 6: Option "navíc", jako možnost, což u některých jazyků je, je k ničemu - pokud je null "občas někde", musím ho čekat všude a tímpádem Option ztrácí význam.
ad 7: stačí jednoduché sum a product types (sorry, nevím, jak se "product type" překládá do češtiny). Praktické hlavně pro jednoduché použití silně typovaných tuples. Praktičnost vylítne do nebes ve spojení s pattern matchingem.
...takže mi do toho sedí spíš Rust než Go ;)
Ad 2: Při překladu nebo za běhu?
-
Teď to neber špatně, ale když jsme tu diskutovali o JavaScriptu, tak standard ani dokumentace nebyly směrodatné, tak mě překvapuje, že máš potřebu to psát do dokumentace. :)
To je nepochopení. Já jsem to vnímal tak, že ty argumentuješ, že "když je to v dokumentaci, tak to není problém - prostě RTFM!". Můj argument byl, že to je kontraintuitivní zhovadilost a to, že to je v dokumentaci, na tom nic nemění.
Makra jsou na některé specialitky velice užitečný nástroj. Zároveň jsou ale nebezpečná v tom, že běžný Franta programátor bude jejich silou tak omámen, že je začne používat na věci, kde reálně potřeba nejsou, a kód se tím objektivně strašně znečitelní a zprasí (odstrašující příklad je Ruby). Tohle nebezpečí se dá snížit váhou manuálu a názoru komunity. Např. v Elixir komunitě tohle povědomí je a ta hrůza, ke které došlo v Ruby světě, se tam neděje.
Souhlasím. Co takové std::optional v C++ ? To je taky "option navíc", nebo je to trošku dál?
Nevím, C++ (už) neznám, ale obecně si myslím, že pokud Option není jediný způsob řešení "prázdných hodnot", tak jako by nebyl vůbec.
Ad 2: Při překladu nebo za běhu?
Ideálně kompletně už při překladu. Ale neumím od stolu tvrdit, že nejsou nějaká zákoutí, která je potřeba aspoň částečně řešit za běhu. Pokud by nebyla, byl by to z mýho pohledu ideální stav.
-
Teď to neber špatně, ale když jsme tu diskutovali o JavaScriptu, tak standard ani dokumentace nebyly směrodatné, tak mě překvapuje, že máš potřebu to psát do dokumentace. :)
To je nepochopení. Já jsem to vnímal tak, že ty argumentuješ, že "když je to v dokumentaci, tak to není problém - prostě RTFM!". Můj argument byl, že to je kontraintuitivní zhovadilost a to, že to je v dokumentaci, na tom nic nemění.
Ok, tak jsem to určitě nemyslel.
Souhlasím. Co takové std::optional v C++ ? To je taky "option navíc", nebo je to trošku dál?
Nevím, C++ (už) neznám, ale obecně si myslím, že pokud Option není jediný způsob řešení "prázdných hodnot", tak jako by nebyl vůbec.
Ono je to dejme tomu jako Maybe v Haskellu. Je to sám o sobě typ, tj. to svým způsobem vynutí s tím "Maybe" pracovat správně. Ale ano, null u pointerů se tím člověk nevyhne, a není to třeba tak hezké jako v tom Haskellu.
-
Ono je to dejme tomu jako Maybe v Haskellu. Je to sám o sobě typ, tj. to svým způsobem vynutí s tím "Maybe" pracovat správně. Ale ano, null u pointerů se tím člověk nevyhne, a není to třeba tak hezké jako v tom Haskellu.
Z mýho pohledu nemusí být Option nějak zvlášť "hezký". Je to prostě parametrizovaný sum type jako jakýkoliv jiný (který v jazyce tak jako tak chci - tj. Option je speciální jenom v tom, že je ve standardní knihovně a používá se všude).
Za nutný ale považuju, aby byl všude, protože jenom tak má smysl - protože za hlavní smysl Option považuju striktní oddělení kódu, kde null může být a kde už ne (už jsem ho ošetřil, dál nemohl probublat). Pokud je Option optional ( ;) ), tak typový systém neumí tyhle dvě situace odlišit, čili nemá informaci, kterou by (snadno) mít mohl a úplně zbytečně ztrácí sílu.
Stejnýho efektu se dá dosáhnout i nějakým atributem "nullable", ale to mi přijde jako zbytečný zaplevelování syntaxe, protože parametrizované sum types chceme tak jako tak...
-
Jo, ještě jedna podstatná věc: raději bych viděl strukturální než nominální typování. Mj. proto, že nominální se dá v případě potřeby snadno simulovat právě pomocí product types, ale naopak ne (když mám nominální typování, jsem ve zlaté kleci, ze které není úniku ;) ).
-
Ad 2: Při překladu nebo za běhu?
Ideálně kompletně už při překladu. Ale neumím od stolu tvrdit, že nejsou nějaká zákoutí, která je potřeba aspoň částečně řešit za běhu. Pokud by nebyla, byl by to z mýho pohledu ideální stav.
Jsou taková zákoutí, ale IMHO v běžném kódu to jde řešit při překladu. Na šablony v C++ nikdo nenadává.
-
Na šablony v C++ nikdo nenadává.
To je velice smělé tvrzení! ;)
-
Na šablony v C++ nikdo nenadává.
To je velice smělé tvrzení! ;)
Nevím o vážně míněné oprávněné kritice. Neberu argument, že jsou pro BF moc složité ;)
-
Nevím o vážně míněné oprávněné kritice.
Já za nejpádnější považuju tohle: https://codegolf.stackexchange.com/questions/1956/generate-the-longest-error-message-in-c :))
...ale nechci se do C++ moc navážet, jak říkám, rozešli jsme se před nejmíň deseti lety a nepotřebuju se k téhle epizodě z mládí vracet :)
-
Makra jsou na některé specialitky velice užitečný nástroj. Zároveň jsou ale nebezpečná v tom, že běžný Franta programátor bude jejich silou tak omámen, že je začne používat na věci, kde reálně potřeba nejsou, a kód se tím objektivně strašně znečitelní a zprasí (odstrašující příklad je Ruby).
už ti tu několikrát vysvětlili, že v Ruby makra nejsou*. Proč to pořád opakuješ?
* teoreticky AST makra jsou možná, ale nikdo je nepoužívá. Narozdíl od Crystalu nebo Elixiru.
-
už ti tu několikrát vysvětlili, že v Ruby makra nejsou*. Proč to pořád opakuješ?
* teoreticky AST makra jsou možná, ale nikdo je nepoužívá. Narozdíl od Crystalu nebo Elixiru.
Odstrašující je to, jak se používají obraty, které by se v jiném jazyce dělaly makry (DSLka apod.) - tj. všechno, co umožňuje napsat krátký "hezký" kód, u kterého je ale děsně komplikované zjistit, co a jak vlastně dělá.
Makra slouží ke generování kódu před překladem. U jazyka, který se nepřekládá, samozřejmě striktně vzato neexistují, ale existují obraty, pomocí kterých se dosahuje stejného efektu (kód generuje kód, akorát ne před překladem, který tam prostě není).
-
Nevím o vážně míněné oprávněné kritice.
Já za nejpádnější považuju tohle: https://codegolf.stackexchange.com/questions/1956/generate-the-longest-error-message-in-c :))
...ale nechci se do C++ moc navážet, jak říkám, rozešli jsme se před nejmíň deseti lety a nepotřebuju se k téhle epizodě z mládí vracet :)
Fakt sorry, ale koukni na datum. Clang (a pod tlakem konkurence pak i GCC) mají normální chybové hlášky. MSVC moc ne, no, ale to je Microsoft...
-
Makra jsou na některé specialitky velice užitečný nástroj. Zároveň jsou ale nebezpečná v tom, že běžný Franta programátor bude jejich silou tak omámen, že je začne používat na věci, kde reálně potřeba nejsou, a kód se tím objektivně strašně znečitelní a zprasí (odstrašující příklad je Ruby).
už ti tu několikrát vysvětlili, že v Ruby makra nejsou*. Proč to pořád opakuješ?
* teoreticky AST makra jsou možná, ale nikdo je nepoužívá. Narozdíl od Crystalu nebo Elixiru.
“Narozdíl” se píše “na rozdíl”, když už chceš machrovat.
-
Nemyslím to v situacích, kde by šel shared_ptr nahradit pomocí unique nebo move apod. Já se s tím běžně setkávám u grafových algoritmů, kde prostě není jeden vlastník objektu, ale je jich víc rovnocenných. V takovém případě buď použiju shared a daný objekt zmizí s posledním vlastníkem nebo bych si to musel evidovat někde bokem.
Tak je otázkou, jestli by se měly vrcholy vlastnit mezi sebou... U stromů to není problém, ale tam není problém použít unique_ptr. U jiných grafů si dovedu představit, že si je všechny držím v nějaké struktuře a mezi sebou mají jen pointery, nebo seznam id ostatních vrcholů. Se shared_ptr by mohl být problém např. u cyklů, když nad tím přemýšlím...
Představ si to třeba jako když polygony vlastní své nody (a ty jsou sdíleny mezi sousedními polygony). Pokud bych měl evidenci (a tím i životnost) nodů řešit bokem tak to je přesně ten argument, který jsem dal na začátku. S GC mi stačí řešit přímo daný problém a VM se postará o výkon i paměť. V C++ mám na výběr - buď si ten GC musím pro každý algoritmus odsimulovat bokem abych se dostal na úroveň JVM, nebo využiju těch super vlastností C++11, ale poběží to třeba 10x pomaleji. To je ta údajná možnost volby. A tohle se v menší či větší míře děje všude, kde není k dispozici GC. Za to dostanu deterministickou dealokaci, kterou po mě v praxi ještě nikdo nechtěl.
Korektně musim dodat, že JVM přitom sežere násobně víc paměti, ale to mi přijde jako dobrý kompromis. Čas programátora a čas CPU jsou dražší než RAM.
-
Nemyslím to v situacích, kde by šel shared_ptr nahradit pomocí unique nebo move apod. Já se s tím běžně setkávám u grafových algoritmů, kde prostě není jeden vlastník objektu, ale je jich víc rovnocenných. V takovém případě buď použiju shared a daný objekt zmizí s posledním vlastníkem nebo bych si to musel evidovat někde bokem.
Tak je otázkou, jestli by se měly vrcholy vlastnit mezi sebou... U stromů to není problém, ale tam není problém použít unique_ptr. U jiných grafů si dovedu představit, že si je všechny držím v nějaké struktuře a mezi sebou mají jen pointery, nebo seznam id ostatních vrcholů. Se shared_ptr by mohl být problém např. u cyklů, když nad tím přemýšlím...
Představ si to třeba jako když polygony vlastní své nody (a ty jsou sdíleny mezi sousedními polygony). Pokud bych měl evidenci (a tím i životnost) nodů řešit bokem tak to je přesně ten argument, který jsem dal na začátku. S GC mi stačí řešit přímo daný problém a VM se postará o výkon i paměť. V C++ mám na výběr - buď si ten GC musím pro každý algoritmus odsimulovat bokem abych se dostal na úroveň JVM, nebo využiju těch super vlastností C++11, ale poběží to třeba 10x pomaleji. To je ta údajná možnost volby. A tohle se v menší či větší míře děje všude, kde není k dispozici GC. Za to dostanu deterministickou dealokaci, kterou po mě v praxi ještě nikdo nechtěl.
Korektně musim dodat, že JVM přitom sežere násobně víc paměti, ale to mi přijde jako dobrý kompromis. Čas programátora a čas CPU jsou dražší než RAM.
Ze se ale za tu RAM vzdycky tolik plati, kdyz si nekde porizujes virtualni server. Porovnej treba hostingy PHP vs Java :) No nevim nevim, do jake miry to porekadlo s vykonem a ram plati.
Ja jeste doted zadny svuj virtualni server nemam - protoze delam v Jave a stalo by me to tak moc penez, si neco jen tak ze srandy koupit...
Kdybych delal v PHP, tak mam hosting lusknutim prstu, protoze to stoji par slupek.
-
Naopak, to je nejvetsi pain GC a to je proc pri serioznim pouziti jsou s Javou obrovske problemy. Nikdy nevis kdy zacne GC uvolnovat a jak velkou kupu pameti. Vede to casto k tomu, ze jakekoli vetsi aplikace se v jave ladi daleko pracneji nez i ve starem C.
Popravdě jsem nikdy nedělal v Javě nic tak velkého. Zatím mi vždy stačil přístup napsat to jednoduše, hlavně pustit z hlavy poučky o mikro optimalizacích z C++ a v případě problému to zprofilovat.
Docela mě zajímá, jaké charakteristiky má taková větší aplikace u které dělá problém spuštění GC.
Vetsi databaze, aplikace citlive na latenci.
-
Ze se ale za tu RAM vzdycky tolik plati, kdyz si nekde porizujes virtualni server. Porovnej treba hostingy PHP vs Java :) No nevim nevim, do jake miry to porekadlo s vykonem a ram plati.
Ja jeste doted zadny svuj virtualni server nemam - protoze delam v Jave a stalo by me to tak moc penez, si neco jen tak ze srandy koupit...
Kdybych delal v PHP, tak mam hosting lusknutim prstu, protoze to stoji par slupek.
Prosimtě ty děláš jakoby to byly nějaký horentní sumy a jako kdyby java automaticky žrala nějaký giga. Virtuál s 2GB RAM stojí asi 1500kč/rok (to jsou 2 hodiny programátora). Já na tom provozuju web, mysql a java server, na který je připojeno několik set klientů, momentálně celý systém žere 375MB RAM.
-
Nemyslím to v situacích, kde by šel shared_ptr nahradit pomocí unique nebo move apod. Já se s tím běžně setkávám u grafových algoritmů, kde prostě není jeden vlastník objektu, ale je jich víc rovnocenných. V takovém případě buď použiju shared a daný objekt zmizí s posledním vlastníkem nebo bych si to musel evidovat někde bokem.
Tak je otázkou, jestli by se měly vrcholy vlastnit mezi sebou... U stromů to není problém, ale tam není problém použít unique_ptr. U jiných grafů si dovedu představit, že si je všechny držím v nějaké struktuře a mezi sebou mají jen pointery, nebo seznam id ostatních vrcholů. Se shared_ptr by mohl být problém např. u cyklů, když nad tím přemýšlím...
Představ si to třeba jako když polygony vlastní své nody (a ty jsou sdíleny mezi sousedními polygony). Pokud bych měl evidenci (a tím i životnost) nodů řešit bokem tak to je přesně ten argument, který jsem dal na začátku.
No nevím, proč pořád používat shared_ptr, když je vlastní polygony. :)
S GC mi stačí řešit přímo daný problém a VM se postará o výkon i paměť. V C++ mám na výběr - buď si ten GC musím pro každý algoritmus odsimulovat bokem abych se dostal na úroveň JVM, nebo využiju těch super vlastností C++11, ale poběží to třeba 10x pomaleji. To je ta údajná možnost volby. A tohle se v menší či větší míře děje všude, kde není k dispozici GC. Za to dostanu deterministickou dealokaci, kterou po mě v praxi ještě nikdo nechtěl.
Korektně musim dodat, že JVM přitom sežere násobně víc paměti, ale to mi přijde jako dobrý kompromis. Čas programátora a čas CPU jsou dražší než RAM.
Tady se neshodneme na tom, že to v C++ poběží 10x pomaleji. Vývoj pravděpodobně trvat déle bude, ale výsledkem bude lepší kontrola nad pamětí i strojovým časem.
Jako jistě, pokud se k tomu přistupuje stylem "další cluster je levnější než to napsat líp", tak ano, ale ty zdroje nejdou škálovat donekonečna a někde ani škálovat nejdou. :)
Jinak jen aby bylo jasno, já nezpochybňuju GC, ani proti nim nic nemám. Ale zatracovat kvůli nim ostatní přístupy, které také mají nesporné výhody, mi přijde mimo.
-
Naopak, to je nejvetsi pain GC a to je proc pri serioznim pouziti jsou s Javou obrovske problemy. Nikdy nevis kdy zacne GC uvolnovat a jak velkou kupu pameti. Vede to casto k tomu, ze jakekoli vetsi aplikace se v jave ladi daleko pracneji nez i ve starem C.
Popravdě jsem nikdy nedělal v Javě nic tak velkého. Zatím mi vždy stačil přístup napsat to jednoduše, hlavně pustit z hlavy poučky o mikro optimalizacích z C++ a v případě problému to zprofilovat.
Docela mě zajímá, jaké charakteristiky má taková větší aplikace u které dělá problém spuštění GC.
Vetsi databaze, aplikace citlive na latenci.
Odkdy má velikost databáze vliv na latenci GC?
-
Naopak, to je nejvetsi pain GC a to je proc pri serioznim pouziti jsou s Javou obrovske problemy. Nikdy nevis kdy zacne GC uvolnovat a jak velkou kupu pameti. Vede to casto k tomu, ze jakekoli vetsi aplikace se v jave ladi daleko pracneji nez i ve starem C.
Popravdě jsem nikdy nedělal v Javě nic tak velkého. Zatím mi vždy stačil přístup napsat to jednoduše, hlavně pustit z hlavy poučky o mikro optimalizacích z C++ a v případě problému to zprofilovat.
Docela mě zajímá, jaké charakteristiky má taková větší aplikace u které dělá problém spuštění GC.
Vetsi databaze, aplikace citlive na latenci.
Odkdy má velikost databáze vliv na latenci GC?
Co to meles? Ja rikam neco jineho, ja rikam, ze velke db v jave s tim maji problem s gc, s world stopem, napr HBase, asi nejrozsirenejsi free big data databaze. A totez i aplikace citlive na latenci. Jakekoli spusteni GC znamena nedeterministicke lagy.
-
Naopak, to je nejvetsi pain GC a to je proc pri serioznim pouziti jsou s Javou obrovske problemy. Nikdy nevis kdy zacne GC uvolnovat a jak velkou kupu pameti. Vede to casto k tomu, ze jakekoli vetsi aplikace se v jave ladi daleko pracneji nez i ve starem C.
Popravdě jsem nikdy nedělal v Javě nic tak velkého. Zatím mi vždy stačil přístup napsat to jednoduše, hlavně pustit z hlavy poučky o mikro optimalizacích z C++ a v případě problému to zprofilovat.
Docela mě zajímá, jaké charakteristiky má taková větší aplikace u které dělá problém spuštění GC.
Vetsi databaze, aplikace citlive na latenci.
Odkdy má velikost databáze vliv na latenci GC?
Co to meles? Ja rikam neco jineho, ja rikam, ze velke db v jave s tim maji problem s gc, s world stopem, napr HBase, asi nejrozsirenejsi free big data databaze. A totez i aplikace citlive na latenci. Jakekoli spusteni GC znamena nedeterministicke lagy.
Jaký má tedy velikost DB vliv na spouštění GC? Podle mne to spolu nesouvisí. DB mohu mít velkou jak chci a na GC to nebude mít vliv.
-
...
Možná pomůže si znovu přečíst jeho příspěvek a všimnout si čárky. :)
-
Možná pomůže si znovu přečíst jeho příspěvek a všimnout si čárky. :)
...
Docela mě zajímá, jaké charakteristiky má taková větší aplikace u které dělá problém spuštění GC.
Vetsi databaze, aplikace citlive na latenci.
Otázka byla, jaké charakteristiky má aplikace, u které dělá problém spuštění GC. Odpovědí bylo, že větší databáze a aplikace citlivé na latenci. Ty citlivé aplikace chápu, ale ptám se: Proč ty větší databáze? Není jedno, zda má databáze 10 KB nebo 10 TB? Na GC aplikace to přece nemůže mít vliv.
-
Představ si to třeba jako když polygony vlastní své nody (a ty jsou sdíleny mezi sousedními polygony). Pokud bych měl evidenci (a tím i životnost) nodů řešit bokem tak to je přesně ten argument, který jsem dal na začátku.
No nevím, proč pořád používat shared_ptr, když je vlastní polygony. :)
Promiň, ale začíná mi připadat, že se nějak zásadně nechápeme ohledně využití shared_ptr.
Když je objekt "rovnocenně" vlastněn několika jinými, tak potřebuju evidovat, že ho stále někdo drží a až zmizí poslední vlastník, tak musí být uvolněn i ten objekt. Pokud mám GC, tak mi tuto evidenci pořeší a mám to bez práce. Pokud ne, tak se přímo k tomu nabízí shared_ptr, protože přesně tohle dělá, akorát je to dost drahé. Mohl bych si to dělat ručně někde bokem, ale v zásadě bych jen simuloval to počítadlo referencí co už je v tom shared, takže by to nejspíš bylo ještě dražší.
-
Představ si to třeba jako když polygony vlastní své nody (a ty jsou sdíleny mezi sousedními polygony). Pokud bych měl evidenci (a tím i životnost) nodů řešit bokem tak to je přesně ten argument, který jsem dal na začátku.
No nevím, proč pořád používat shared_ptr, když je vlastní polygony. :)
Promiň, ale začíná mi připadat, že se nějak zásadně nechápeme ohledně využití shared_ptr.
Když je objekt "rovnocenně" vlastněn několika jinými, tak potřebuju evidovat, že ho stále někdo drží a až zmizí poslední vlastník, tak musí být uvolněn i ten objekt. Pokud mám GC, tak mi tuto evidenci pořeší a mám to bez práce. Pokud ne, tak se přímo k tomu nabízí shared_ptr, protože přesně tohle dělá, akorát je to dost drahé. Mohl bych si to dělat ručně někde bokem, ale v zásadě bych jen simuloval to počítadlo referencí co už je v tom shared, takže by to nejspíš bylo ještě dražší.
Ano, ale pokud polygony vlastní své nody, tak proč by je neměly vlastnit přes unique_ptr a ostatním dávat obyčejný pointer?
Ano, GC to za tebe pořeší a ty to máš bez práce... ale jak to ten GC pod kapotou asi dělá...? :)
-
Promiň, ale začíná mi připadat, že se nějak zásadně nechápeme ohledně využití shared_ptr.
Když je objekt "rovnocenně" vlastněn několika jinými, tak potřebuju evidovat, že ho stále někdo drží a až zmizí poslední vlastník, tak musí být uvolněn i ten objekt. Pokud mám GC, tak mi tuto evidenci pořeší a mám to bez práce. Pokud ne, tak se přímo k tomu nabízí shared_ptr, protože přesně tohle dělá, akorát je to dost drahé. Mohl bych si to dělat ručně někde bokem, ale v zásadě bych jen simuloval to počítadlo referencí co už je v tom shared, takže by to nejspíš bylo ještě dražší.
Ano, ale pokud polygony vlastní své nody, tak proč by je neměly vlastnit přes unique_ptr a ostatním dávat obyčejný pointer?
Ano, GC to za tebe pořeší a ty to máš bez práce... ale jak to ten GC pod kapotou asi dělá...? :)
Když polygon uvolní svůj node, tak všechny pointery na něj se stanou nefunkčními. Proto je zpravidla výhodnější shared_ptr. Při zániku polygonu zaniknou jen ty nody, které už nemají žádnou další referenci.
shared_ptr je vlastně mezivrstvou obsluhovanou na úrovni jazyka, syntaktickým cukrem. Když si to někdo chce obsloužit ve vlastní režii, ...
GC bych do toho netahal, ten s tím nemá nic společného.
-
Představ si to třeba jako když polygony vlastní své nody (a ty jsou sdíleny mezi sousedními polygony). Pokud bych měl evidenci (a tím i životnost) nodů řešit bokem tak to je přesně ten argument, který jsem dal na začátku.
No nevím, proč pořád používat shared_ptr, když je vlastní polygony. :)
Promiň, ale začíná mi připadat, že se nějak zásadně nechápeme ohledně využití shared_ptr.
Když je objekt "rovnocenně" vlastněn několika jinými, tak potřebuju evidovat, že ho stále někdo drží a až zmizí poslední vlastník, tak musí být uvolněn i ten objekt. Pokud mám GC, tak mi tuto evidenci pořeší a mám to bez práce. Pokud ne, tak se přímo k tomu nabízí shared_ptr, protože přesně tohle dělá, akorát je to dost drahé. Mohl bych si to dělat ručně někde bokem, ale v zásadě bych jen simuloval to počítadlo referencí co už je v tom shared, takže by to nejspíš bylo ještě dražší.
Ano, ale pokud polygony vlastní své nody, tak proč by je neměly vlastnit přes unique_ptr a ostatním dávat obyčejný pointer?
Ano, GC to za tebe pořeší a ty to máš bez práce... ale jak to ten GC pod kapotou asi dělá...? :)
Když více polygonů vlastní jeden node, tak ho nemůžou mít přes unique, to je v rozporu.
Když tam budu dávat obyčejný pointer, tak si musím někde počítat reference, což je ještě horší než sharedptr.
GC to dělá mnohem efektivněji, v tom je ten vtip a to se tady už od včerejška snažím říci. Stručně řečeno, nepočítá reference, chodí jen po přeživších a má výraznou “množstevní slevu”.
-
Představ si to třeba jako když polygony vlastní své nody (a ty jsou sdíleny mezi sousedními polygony). Pokud bych měl evidenci (a tím i životnost) nodů řešit bokem tak to je přesně ten argument, který jsem dal na začátku.
No nevím, proč pořád používat shared_ptr, když je vlastní polygony. :)
Promiň, ale začíná mi připadat, že se nějak zásadně nechápeme ohledně využití shared_ptr.
Když je objekt "rovnocenně" vlastněn několika jinými, tak potřebuju evidovat, že ho stále někdo drží a až zmizí poslední vlastník, tak musí být uvolněn i ten objekt. Pokud mám GC, tak mi tuto evidenci pořeší a mám to bez práce. Pokud ne, tak se přímo k tomu nabízí shared_ptr, protože přesně tohle dělá, akorát je to dost drahé. Mohl bych si to dělat ručně někde bokem, ale v zásadě bych jen simuloval to počítadlo referencí co už je v tom shared, takže by to nejspíš bylo ještě dražší.
Ano, ale pokud polygony vlastní své nody, tak proč by je neměly vlastnit přes unique_ptr a ostatním dávat obyčejný pointer?
Ano, GC to za tebe pořeší a ty to máš bez práce... ale jak to ten GC pod kapotou asi dělá...? :)
Když více polygonů vlastní jeden node, tak ho nemůžou mít přes unique, to je v rozporu.
Když tam budu dávat obyčejný pointer, tak si musím někde počítat reference, což je ještě horší než sharedptr.
GC to dělá mnohem efektivněji, v tom je ten vtip a to se tady už od včerejška snažím říci. Stručně řečeno, nepočítá reference, chodí jen po přeživších a má výraznou “množstevní slevu”.
Pokud více polygonů vlastní jeden node, tak už nejsou jeho, ale všech. Tj. to úplně neodpovídá tomu, co jsi na začátku psal. Takže ano, pak není shared_ptr od věci. Nebo se to taky dá udělat tak, jak jsem psal předtím - vlastnit to bude nějaká nadstruktura nad tím, která to v případě potřeby uklidí.
S tou efektivitou GC bych byl opatrný, protože hodně také záleží, jak funguje vevnitř. V čem přesně myslíš, že má "množstevní slevu"?
-
Představ si to třeba jako když polygony vlastní své nody (a ty jsou sdíleny mezi sousedními polygony). Pokud bych měl evidenci (a tím i životnost) nodů řešit bokem tak to je přesně ten argument, který jsem dal na začátku.
No nevím, proč pořád používat shared_ptr, když je vlastní polygony. :)
Promiň, ale začíná mi připadat, že se nějak zásadně nechápeme ohledně využití shared_ptr.
Když je objekt "rovnocenně" vlastněn několika jinými, tak potřebuju evidovat, že ho stále někdo drží a až zmizí poslední vlastník, tak musí být uvolněn i ten objekt. Pokud mám GC, tak mi tuto evidenci pořeší a mám to bez práce. Pokud ne, tak se přímo k tomu nabízí shared_ptr, protože přesně tohle dělá, akorát je to dost drahé. Mohl bych si to dělat ručně někde bokem, ale v zásadě bych jen simuloval to počítadlo referencí co už je v tom shared, takže by to nejspíš bylo ještě dražší.
Ano, ale pokud polygony vlastní své nody, tak proč by je neměly vlastnit přes unique_ptr a ostatním dávat obyčejný pointer?
Ano, GC to za tebe pořeší a ty to máš bez práce... ale jak to ten GC pod kapotou asi dělá...? :)
tak si musím někde počítat reference, což je ještě horší než sharedptr.
GC to dělá mnohem efektivněji
Optimalizované počítání referencí bývá efektivnější, jednak se nic nepočítá, když čítač nepřekročí 1 (objekty s krátkou životností, obdoba nejnižší generace v tracing GC), a za druhé statická analýza odhalí případy, kdy není nutné čítač upravovat, i když by to dávalo smysl, například při návratu z funkce, kde se typicky dělá retain a následný (auto)release hned po sobě, což překladač odhalí a vyhodí. Kupříkladu Swift takto funguje a je to efektivnější než tracing GC. C++ má úplně jiný přístup s move sémantikou, ale to už tu psali jiní.
-
Když více polygonů vlastní jeden node, tak ho nemůžou mít přes unique, to je v rozporu.
Když tam budu dávat obyčejný pointer, tak si musím někde počítat reference, což je ještě horší než sharedptr.
GC to dělá mnohem efektivněji, v tom je ten vtip a to se tady už od včerejška snažím říci. Stručně řečeno, nepočítá reference, chodí jen po přeživších a má výraznou “množstevní slevu”.
Pokud více polygonů vlastní jeden node, tak už nejsou jeho, ale všech. Tj. to úplně neodpovídá tomu, co jsi na začátku psal. Takže ano, pak není shared_ptr od věci. Nebo se to taky dá udělat tak, jak jsem psal předtím - vlastnit to bude nějaká nadstruktura nad tím, která to v případě potřeby uklidí.
S tou efektivitou GC bych byl opatrný, protože hodně také záleží, jak funguje vevnitř. V čem přesně myslíš, že má "množstevní slevu"?
Když to bude vlastnit nadstruktura, tak jak pozná, že může vyhodit nepoužívané nody? Jasně, řešení známe, ptám se spíš proto aby sis rozmyslel, jestli to opravdu dovedeš řešit lépe než těmi sdílenými pointery.
Typický GC má "množstevní slevu" proto, že se nestará o nedosažitelné objekty, řeší jen živé. To znamená, že čím déle vydrží neběžet, tím víc bude těch mrtvých a tím víc na tom ušetří. V důsledku toho je velmi levné dělat krátce žijící objekty, což má spoustu dalších pozitivních důsledků. Naopak new/delete se poctivě stará o každý objekt, což je dost neefektivní a v důsledku nutí programátora, aby se krátkodobým objektům vyhnul.
Kdysi jsem si potřeboval ověřit, jak moc v javě můžu prasit a bezohledně alokovat nové a vyšlo mi, že má asi desetinásobnou průchodnost proti MS C++. Čili za daný čas dokáže alokovat a zahodit 10x víc objektů než C++.
-
Ze se ale za tu RAM vzdycky tolik plati, kdyz si nekde porizujes virtualni server. Porovnej treba hostingy PHP vs Java :) No nevim nevim, do jake miry to porekadlo s vykonem a ram plati.
Ja jeste doted zadny svuj virtualni server nemam - protoze delam v Jave a stalo by me to tak moc penez, si neco jen tak ze srandy koupit...
Kdybych delal v PHP, tak mam hosting lusknutim prstu, protoze to stoji par slupek.
Prosimtě ty děláš jakoby to byly nějaký horentní sumy a jako kdyby java automaticky žrala nějaký giga. Virtuál s 2GB RAM stojí asi 1500kč/rok (to jsou 2 hodiny programátora). Já na tom provozuju web, mysql a java server, na který je připojeno několik set klientů, momentálně celý systém žere 375MB RAM.
No, ono 100x nic zabilo vola. Ja si myslim, ze na vykonu a zravosti zalezi. Je to o tech detailech. Dam dalsi priklad - jak dlouho startuje Spring, treba pri JUnit springovych testech? To vsechno tak nejak s tou zravosti Javy souvisi.
A kde ti to stoji 1500,- za rok? U active24 je VPS 2GB za 400,- mesic, mel jsem VPS u Forpsi, tam jsem mel jen 1GB a stalo to pulku. Tak to by me zajimalo, kde jsi narazil na takovy kauf - 2gb jen za 1500,-. Moc ti neverim.
-
Optimalizované počítání referencí bývá efektivnější, jednak se nic nepočítá, když čítač nepřekročí 1 (objekty s krátkou životností, obdoba nejnižší generace v tracing GC), a za druhé statická analýza odhalí případy, kdy není nutné čítač upravovat, i když by to dávalo smysl, například při návratu z funkce, kde se typicky dělá retain a následný (auto)release hned po sobě, což překladač odhalí a vyhodí. Kupříkladu Swift takto funguje a je to efektivnější než tracing GC. C++ má úplně jiný přístup s move sémantikou, ale to už tu psali jiní.
Velkou režii má shared_ptr v C++ kvůli thread-safe control bloku, to vyžaduje synchronizaci mezi jádry při každé změně čítače. Přitom je to úplně zbytečné dělat, pokud instanci shared_ptr používá jen jeden thread. Thread-unsafe shared_ptr s nižší režií ve standardní knihovně C++ není. Myslím, že je to škoda a měla by být možnost volby, třeba Rust má obě varianty, Rc a Arc.
-
Dam dalsi priklad - jak dlouho startuje Spring, treba pri JUnit springovych testech? To vsechno tak nejak s tou zravosti Javy souvisi.
Jak dlouho startují JUnit testy bez Springu? Jak dlouho startuje Micronaut? To je nějaká jiná Java?
-
Dam dalsi priklad - jak dlouho startuje Spring, treba pri JUnit springovych testech? To vsechno tak nejak s tou zravosti Javy souvisi.
Jak dlouho startují JUnit testy bez Springu?
Dost dlouho, na to ze je to jenom takovy psouk :-) A kdyz je tech psouku 1000 (Spring), tak uz to jde i dost videt. Proc to vubec resime toto. Kazdy proste vime, ze Java je pomala a nenazrana, pomalu startuje, v provnani treba s Go, nebo Swift, nebo Objective-C.
-
Ze se ale za tu RAM vzdycky tolik plati, kdyz si nekde porizujes virtualni server. Porovnej treba hostingy PHP vs Java :) No nevim nevim, do jake miry to porekadlo s vykonem a ram plati.
Ja jeste doted zadny svuj virtualni server nemam - protoze delam v Jave a stalo by me to tak moc penez, si neco jen tak ze srandy koupit...
Kdybych delal v PHP, tak mam hosting lusknutim prstu, protoze to stoji par slupek.
Prosimtě ty děláš jakoby to byly nějaký horentní sumy a jako kdyby java automaticky žrala nějaký giga. Virtuál s 2GB RAM stojí asi 1500kč/rok (to jsou 2 hodiny programátora). Já na tom provozuju web, mysql a java server, na který je připojeno několik set klientů, momentálně celý systém žere 375MB RAM.
No, ono 100x nic zabilo vola. Ja si myslim, ze na vykonu a zravosti zalezi. Je to o tech detailech. Dam dalsi priklad - jak dlouho startuje Spring, treba pri JUnit springovych testech? To vsechno tak nejak s tou zravosti Javy souvisi.
A kde ti to stoji 1500,- za rok? U active24 je VPS 2GB za 400,- mesic, mel jsem VPS u Forpsi, tam jsem mel jen 1GB a stalo to pulku. Tak to by me zajimalo, kde jsi narazil na takovy kauf - 2gb jen za 1500,-. Moc ti neverim.
Nevím, spring nepoužívám. A pokud to je líný, tak tím spíš to nepoužiju.
Virtuál mám u Wedosu. V akci to bývá nějakých 800 ročně, jinak kolem těch 1500.
-
Proc to vubec resime toto. Kazdy proste vime, ze Java je pomala a nenazrana, pomalu startuje, v provnani treba s Go, nebo Swift, nebo Objective-C.
Ne, to „víte“ jenom vy. A řešíme to proto, abyste se dozvěděl, jak je to doopravdy. Když provedení aplikace (https://www.root.cz/clanky/nechte-go-plavat-ted-svisti-java/) v Go trvá průměrně 260 ms a doba běhu aplikace (s tím samým algoritmem) v Javě trvá průměrně 166 ms, nevypadá to, že by Java byla v porovnání s Go pomalejší. Spíš to vypadá opačně.
-
Dost dlouho, na to ze je to jenom takovy psouk :-) A kdyz je tech psouku 1000 (Spring), tak uz to jde i dost videt. Proc to vubec resime toto. Kazdy proste vime, ze Java je pomala a nenazrana, pomalu startuje, v provnani treba s Go, nebo Swift, nebo Objective-C.
Zrovna Spring nie je dokazom toho, ze Java je pomala. Keby bolo mozne Spring sprasit aj s tym masivnym znasilnovanim reflexie, ako to len on vie, v akomkolvek jazyku, bolo by to rovnako pomale...a nenazrane.
Mne sa napriklad dost paci novy hybridny pamatovy model, ktory do Javy pribudol. Zneuzitie TLABov na region based memory model, z toho plynuce znizenie pamatovych narokov, z toho plynuca GC-less dealokacia, nezavisle heapy, automaticka multitenancy, milisekundovy startup time... lahka integracia s C/C++. ved to je vsetko super, nie? Nechapem preco sa ludia z toho netesia.
-
Když více polygonů vlastní jeden node, tak ho nemůžou mít přes unique, to je v rozporu.
Když tam budu dávat obyčejný pointer, tak si musím někde počítat reference, což je ještě horší než sharedptr.
GC to dělá mnohem efektivněji, v tom je ten vtip a to se tady už od včerejška snažím říci. Stručně řečeno, nepočítá reference, chodí jen po přeživších a má výraznou “množstevní slevu”.
Pokud více polygonů vlastní jeden node, tak už nejsou jeho, ale všech. Tj. to úplně neodpovídá tomu, co jsi na začátku psal. Takže ano, pak není shared_ptr od věci. Nebo se to taky dá udělat tak, jak jsem psal předtím - vlastnit to bude nějaká nadstruktura nad tím, která to v případě potřeby uklidí.
S tou efektivitou GC bych byl opatrný, protože hodně také záleží, jak funguje vevnitř. V čem přesně myslíš, že má "množstevní slevu"?
Když to bude vlastnit nadstruktura, tak jak pozná, že může vyhodit nepoužívané nody? Jasně, řešení známe, ptám se spíš proto aby sis rozmyslel, jestli to opravdu dovedeš řešit lépe než těmi sdílenými pointery.
Tak tam záleží, jak to bude navrhnuto. Ale typicky přes counter, nebo nějaký vektor. Myslím, že rychlostně by si člověk mohl pomoct.
Typický GC má "množstevní slevu" proto, že se nestará o nedosažitelné objekty, řeší jen živé. To znamená, že čím déle vydrží neběžet, tím víc bude těch mrtvých a tím víc na tom ušetří. V důsledku toho je velmi levné dělat krátce žijící objekty, což má spoustu dalších pozitivních důsledků. Naopak new/delete se poctivě stará o každý objekt, což je dost neefektivní a v důsledku nutí programátora, aby se krátkodobým objektům vyhnul.
Jak se stará jen o živé? Ten overhead u GC nevadí? A proč by v C++ mělo být velmi drahé dělat krátce žijící objekty? Copak si nemůžu naalokovat paměť, kterou pak budu předávat, abych nemusel pořád alokovat a dealokovat?
Kdysi jsem si potřeboval ověřit, jak moc v javě můžu prasit a bezohledně alokovat nové a vyšlo mi, že má asi desetinásobnou průchodnost proti MS C++. Čili za daný čas dokáže alokovat a zahodit 10x víc objektů než C++.
Tak zrovna MS C++ není úplně vypovídající. Také by mě zajímalo, jak jsi to měřil. :)
-
Když více polygonů vlastní jeden node, tak ho nemůžou mít přes unique, to je v rozporu.
Když tam budu dávat obyčejný pointer, tak si musím někde počítat reference, což je ještě horší než sharedptr.
GC to dělá mnohem efektivněji, v tom je ten vtip a to se tady už od včerejška snažím říci. Stručně řečeno, nepočítá reference, chodí jen po přeživších a má výraznou “množstevní slevu”.
Pokud více polygonů vlastní jeden node, tak už nejsou jeho, ale všech. Tj. to úplně neodpovídá tomu, co jsi na začátku psal. Takže ano, pak není shared_ptr od věci. Nebo se to taky dá udělat tak, jak jsem psal předtím - vlastnit to bude nějaká nadstruktura nad tím, která to v případě potřeby uklidí.
S tou efektivitou GC bych byl opatrný, protože hodně také záleží, jak funguje vevnitř. V čem přesně myslíš, že má "množstevní slevu"?
Když to bude vlastnit nadstruktura, tak jak pozná, že může vyhodit nepoužívané nody? Jasně, řešení známe, ptám se spíš proto aby sis rozmyslel, jestli to opravdu dovedeš řešit lépe než těmi sdílenými pointery.
Tak tam záleží, jak to bude navrhnuto. Ale typicky přes counter, nebo nějaký vektor. Myslím, že rychlostně by si člověk mohl pomoct.
Typický GC má "množstevní slevu" proto, že se nestará o nedosažitelné objekty, řeší jen živé. To znamená, že čím déle vydrží neběžet, tím víc bude těch mrtvých a tím víc na tom ušetří. V důsledku toho je velmi levné dělat krátce žijící objekty, což má spoustu dalších pozitivních důsledků. Naopak new/delete se poctivě stará o každý objekt, což je dost neefektivní a v důsledku nutí programátora, aby se krátkodobým objektům vyhnul.
Jak se stará jen o živé? Ten overhead u GC nevadí? A proč by v C++ mělo být velmi drahé dělat krátce žijící objekty? Copak si nemůžu naalokovat paměť, kterou pak budu předávat, abych nemusel pořád alokovat a dealokovat?
Kdysi jsem si potřeboval ověřit, jak moc v javě můžu prasit a bezohledně alokovat nové a vyšlo mi, že má asi desetinásobnou průchodnost proti MS C++. Čili za daný čas dokáže alokovat a zahodit 10x víc objektů než C++.
Tak zrovna MS C++ není úplně vypovídající. Také by mě zajímalo, jak jsi to měřil. :)
Ano, můžeš zkoušet různé countery, vektory, předalokace, dealokace a věřit, že tím obejdeš nevýhody new/delete. Však si to někdy zkus. Můj první příspěvek byl právě o tom, že GC je v tomhle přinejmenším dobrý kompromis vzhledem k tomu, jak to usnadňuje práci programátora. Ano, namítáš, že C++ programátor si může vybrat, já říkám, že výkon tohoto mechanismu je tak mizerný, že u složitějších problémů je k tomu donucen.
GC má samozřejmě overhead, ale když na něj dojde, tak často existuje jen zlomek objektů, co prošly tím programem. Pořeší tento zlomek a je hotovo. Delete se poctivě stará o každých pár byte co si někde alokuješ, to je prostě ohromný rozdíl.
Dělal jsem spoustu měření na různě složitých algoritmech. Pro lowlevel benchmark jsem si prostě udělal pole řádově tisíce pointerů a do toho jsem alokoval stále nové objekty. Ty něco triviálního počítaly a pak je přeplácnul nový objekt. Tohle samozřejmě GC chutná nejvíc, takže se tam projeví výhody proti delete.
-
Proc to vubec resime toto. Kazdy proste vime, ze Java je pomala a nenazrana, pomalu startuje, v provnani treba s Go, nebo Swift, nebo Objective-C.
Ne, to „víte“ jenom vy. A řešíme to proto, abyste se dozvěděl, jak je to doopravdy. Když provedení aplikace (https://www.root.cz/clanky/nechte-go-plavat-ted-svisti-java/) v Go trvá průměrně 260 ms a doba běhu aplikace (s tím samým algoritmem) v Javě trvá průměrně 166 ms, nevypadá to, že by Java byla v porovnání s Go pomalejší. Spíš to vypadá opačně.
1. Oracle - kolik bude GraalVM stat, jake bude srani s licencemi?
2. Kompilace - jak dlouho se to kompiluje v porovnani s Go?
3. GraalVM a Spring - nedari se mi vyrobit image - uz se s tim zase seru, hodina a pul zabita.
4. Konfigurace - neni to lusknutim prstu, s Go dam 1x download a mam vse.
5. Kdy bude GraalVM pripraven pro produkcni nasazeni v enterprise?
Tvl ta Java to je takova hydra, a ted ji akorat zase narostla dalsi hlava :D
-
1. Oracle - kolik bude GraalVM stat, jake bude srani s licencemi?
2. Kompilace - jak dlouho se to kompiluje v porovnani s Go?
3. GraalVM a Spring - nedari se mi vyrobit image - uz se s tim zase seru, hodina a pul zabita.
4. Konfigurace - neni to lusknutim prstu, s Go dam 1x download a mam vse.
5. Kdy bude GraalVM pripraven pro produkcni nasazeni v enterprise?
Tvl ta Java to je takova hydra, a ted ji akorat zase narostla dalsi hlava :D
GraalVM Community Edition je pod GPL. Argumenty vás ale zjevně nezajímají, vy máte jasno, a když jsem vaše argumenty vyvrátil, vymyslel jste si jiné – co na tom, že jsou úplně nesmyslné. Nebo vás vážně trápí, kolik sekund trvá vytvoření nativního obrazu někde na build serveru? GraalVM vyšla zrovna před pár dny v oficiální produkční verzi. Pro nasazení v enterprise bude vhodná tak během roku dvou. Na rozdíl od Go, které by pro nasazení v enterprise potřebovalo ještě aspoň tak pět (pokud by se na tom opravdu pořádně makalo) nebo deset let vývoje celého ekosystému. Jenže ve skutečnosti nebude Go pro nasazení do enterprise vhodné nejspíš nikdy, protože k tomu není určené. Java tak sice původně také nebyla zaměřená, ale nemyslím si, že by Go zopakovalo cestu Javy. Největší smůla Go je ta, že se teď na něj lepí takoví ti rozumbradové, kteří jenom opakují memy zaslechnuté někde na internetu, ale ve skutečnosti o tom nic neví.
-
Typický GC má "množstevní slevu" proto, že se nestará o nedosažitelné objekty, řeší jen živé. To znamená, že čím déle vydrží neběžet, tím víc bude těch mrtvých a tím víc na tom ušetří. V důsledku toho je velmi levné dělat krátce žijící objekty, což má spoustu dalších pozitivních důsledků. Naopak new/delete se poctivě stará o každý objekt, což je dost neefektivní a v důsledku nutí programátora, aby se krátkodobým objektům vyhnul.
Jak se stará jen o živé? Ten overhead u GC nevadí? A proč by v C++ mělo být velmi drahé dělat krátce žijící objekty? Copak si nemůžu naalokovat paměť, kterou pak budu předávat, abych nemusel pořád alokovat a dealokovat?
Pokud chceš programovat objektově, tak stále alokuješ nové místo pro objekty, zejména pokud je děláš immutable. Dealokaci neřešíš a tím šetříš čas. Ušetříš tak např. 200 ms a hromadu řádek kódu. Pak paměť dojde a spustí se GC, kterému úklid bude trvat např. 20 ms. Budeš to sice vnímat jako lag, ale celkově jsi ušetřil 180 ms.
-
1. Oracle - kolik bude GraalVM stat, jake bude srani s licencemi?
2. Kompilace - jak dlouho se to kompiluje v porovnani s Go?
3. GraalVM a Spring - nedari se mi vyrobit image - uz se s tim zase seru, hodina a pul zabita.
4. Konfigurace - neni to lusknutim prstu, s Go dam 1x download a mam vse.
5. Kdy bude GraalVM pripraven pro produkcni nasazeni v enterprise?
Tvl ta Java to je takova hydra, a ted ji akorat zase narostla dalsi hlava :D
GraalVM Community Edition je pod GPL. Argumenty vás ale zjevně nezajímají, vy máte jasno, a když jsem vaše argumenty vyvrátil, vymyslel jste si jiné – co na tom, že jsou úplně nesmyslné. Nebo vás vážně trápí, kolik sekund trvá vytvoření nativního obrazu někde na build serveru? GraalVM vyšla zrovna před pár dny v oficiální produkční verzi. Pro nasazení v enterprise bude vhodná tak během roku dvou. Na rozdíl od Go, které by pro nasazení v enterprise potřebovalo ještě aspoň tak pět (pokud by se na tom opravdu pořádně makalo) nebo deset let vývoje celého ekosystému. Jenže ve skutečnosti nebude Go pro nasazení do enterprise vhodné nejspíš nikdy, protože k tomu není určené. Java tak sice původně také nebyla zaměřená, ale nemyslím si, že by Go zopakovalo cestu Javy. Největší smůla Go je ta, že se teď na něj lepí takoví ti rozumbradové, kteří jenom opakují memy zaslechnuté někde na internetu, ale ve skutečnosti o tom nic neví.
Pane Jirsak, me uz asi nepresvedcite. Devil is in the details. Muzete se izolovane zamerit na co chcete, ale Java ukazala v podlednich 2 dekadach co je zac. Je to pomalá těžkotonážní hydra.
Já ještě teď rozdýchávám, jak mi referenční implementace pro JAX-RS, Jersey, přidala 1 vteřinu ke startu servletové Hello World aplikace.
Můžete mít GraalVM, můžete mít cokoliv, ale v Javě se, když se to vezme kolem a kolem, píšou vyrábí hrozné sračky, celá ta platfroma je složitá, stdlib je outdated a musí se furt všechno odněkud natahovat přes Maven, buildy dlouho trvají... a teď celá ta entropie platformy ještě narostla dalším featurou. Java jazyk sucks a lot, Kotlin se skoro nepoužívá, Scala už vůbec ne. Java platforma dojede na bídnou kvalitu a údržbu, je to ja jak stará zaprášená továrna z 90 let, teď do ni vestavěli sice nový blok, ale stejně už každý čeká na to, až se to celé zbourá.
No nic, zitra zase do prace, Java me dobre zivi, v enterprise nic lepsiho neni, ale uz se tesim na neco noveho.
Priznam se, ze vcelku zavidim C#-pistum .NET, ale plnohodnotna nahrada to jeste neni A navic mi C# prijdou jako hrozne nafintene nemehla.
-
Proc to vubec resime toto. Kazdy proste vime, ze Java je pomala a nenazrana, pomalu startuje, v provnani treba s Go, nebo Swift, nebo Objective-C.
Ne, to „víte“ jenom vy. A řešíme to proto, abyste se dozvěděl, jak je to doopravdy. Když provedení aplikace (https://www.root.cz/clanky/nechte-go-plavat-ted-svisti-java/) v Go trvá průměrně 260 ms a doba běhu aplikace (s tím samým algoritmem) v Javě trvá průměrně 166 ms, nevypadá to, že by Java byla v porovnání s Go pomalejší. Spíš to vypadá opačně.
1. Oracle - kolik bude GraalVM stat, jake bude srani s licencemi?
2. Kompilace - jak dlouho se to kompiluje v porovnani s Go?
3. GraalVM a Spring - nedari se mi vyrobit image - uz se s tim zase seru, hodina a pul zabita.
4. Konfigurace - neni to lusknutim prstu, s Go dam 1x download a mam vse.
5. Kdy bude GraalVM pripraven pro produkcni nasazeni v enterprise?
Tvl ta Java to je takova hydra, a ted ji akorat zase narostla dalsi hlava :D
GraalVM nebude na enterprise nasadenie ready nikdy. Je to iba testbed project, ktory sluzi na to, aby sa ludia zacali hrat s buducimi technologiami a poskytli potrebny feedback.
Ak chcete vediet, kedy sa technologie, ktore sa na GraalVM prototypuju, stanu enterprise ready, tak to zavisi od kazdeho downstream projektu zvlast.
Osobne si myslim, ze taky zasadny milnik pre bezneho Frantu bude vtedy, ked vyjde stabilny release MySQL a Oracle DB s MLE. Predpokladam, ze na roote bude nejaky clanok.
Co sa tyka Javy, tak kompilacna infrastruktura z Graalu je sucastou referencnej implementacie uz od 9ny.
-
Jak se stará jen o živé? Ten overhead u GC nevadí? A proč by v C++ mělo být velmi drahé dělat krátce žijící objekty? Copak si nemůžu naalokovat paměť, kterou pak budu předávat, abych nemusel pořád alokovat a dealokovat?
Kdysi jsem si potřeboval ověřit, jak moc v javě můžu prasit a bezohledně alokovat nové a vyšlo mi, že má asi desetinásobnou průchodnost proti MS C++. Čili za daný čas dokáže alokovat a zahodit 10x víc objektů než C++.
Tak zrovna MS C++ není úplně vypovídající. Také by mě zajímalo, jak jsi to měřil. :)
Ano, můžeš zkoušet různé countery, vektory, předalokace, dealokace a věřit, že tím obejdeš nevýhody new/delete. Však si to někdy zkus. Můj první příspěvek byl právě o tom, že GC je v tomhle přinejmenším dobrý kompromis vzhledem k tomu, jak to usnadňuje práci programátora. Ano, namítáš, že C++ programátor si může vybrat, já říkám, že výkon tohoto mechanismu je tak mizerný, že u složitějších problémů je k tomu donucen.
Já si to zkoušel, ale přes unique_ptr. A nenamítám, že programátor v C++ si může vybrat, namítám, že GC není jednoznačnou výhodou a že ne-GC přístup má své nesporné výhody. Navíc nejprve by to chtělo specifikovat, co si představuješ pod pojmem "výkon".
GC má samozřejmě overhead, ale když na něj dojde, tak často existuje jen zlomek objektů, co prošly tím programem. Pořeší tento zlomek a je hotovo. Delete se poctivě stará o každých pár byte co si někde alokuješ, to je prostě ohromný rozdíl.
Dělal jsem spoustu měření na různě složitých algoritmech. Pro lowlevel benchmark jsem si prostě udělal pole řádově tisíce pointerů a do toho jsem alokoval stále nové objekty. Ty něco triviálního počítaly a pak je přeplácnul nový objekt. Tohle samozřejmě GC chutná nejvíc, takže se tam projeví výhody proti delete.
Obávám se, že tohle nechápu. Zaprvé pokud existuje zlomek objektů, co prošly programem, tak proč bych měl v ne-GC přístupu alokovat víc? Zadruhé ten GC se taky stará o každý byte, protože nechce mít memory-leaky (a ten delete volá taky) - hlavní rozdíl je "kdy" a "jak často".
Ad ten příklad - pokud sis udělal řádově tisíce pointerů a do nich alokoval stále nové objekty, které něco počítaly a pak je přeplácnul, tak nechápu, proč jsi volal delete. Delete voláš až na konci, proč bych dealokoval paměť, kterou budu vzápětí alokovat? To potom chápu, že to pro GC vyházelo lépe, ale v takovém případě je problém jinde... :)
-
GraalVM nebude na enterprise nasadenie ready nikdy. Je to iba testbed project, ktory sluzi na to, aby sa ludia zacali hrat s buducimi technologiami a poskytli potrebny feedback.
Ak chcete vediet, kedy sa technologie, ktore sa na GraalVM prototypuju, stanu enterprise ready, tak to zavisi od kazdeho downstream projektu zvlast.
Je to přesně opačně. GraalVM je marketingové označení od Oraclu, který vzal několik souvisejících projektů a spojil je pod tímhle názvem, aby to mohl prodávat do enterprise sféry.
Ano, většina těch projektů je velmi mladých, ale mají našlápnuto velice dobře, správným směrem, a ukazuje to, že to Oracle s Javou myslí vážně. A že si s ní ví rady, na rozdíl od Sunu.
Pane Jirsak, me uz asi nepresvedcite.
Ano, to už jsem pochopil. Mně je to jedno, já vás přesvědčit nechci. Akorát bych byl velmi nerad, abych takhle jednou dopadl sám – že pro mne můj názor bude tak důležitý, že nebudu ochoten přijmout jakékoli argumenty proti.
-
Když více polygonů vlastní jeden node, tak ho nemůžou mít přes unique, to je v rozporu.
Když tam budu dávat obyčejný pointer, tak si musím někde počítat reference, což je ještě horší než sharedptr.
GC to dělá mnohem efektivněji, v tom je ten vtip a to se tady už od včerejška snažím říci. Stručně řečeno, nepočítá reference, chodí jen po přeživších a má výraznou “množstevní slevu”.
Pokud více polygonů vlastní jeden node, tak už nejsou jeho, ale všech. Tj. to úplně neodpovídá tomu, co jsi na začátku psal. Takže ano, pak není shared_ptr od věci. Nebo se to taky dá udělat tak, jak jsem psal předtím - vlastnit to bude nějaká nadstruktura nad tím, která to v případě potřeby uklidí.
S tou efektivitou GC bych byl opatrný, protože hodně také záleží, jak funguje vevnitř. V čem přesně myslíš, že má "množstevní slevu"?
Když to bude vlastnit nadstruktura, tak jak pozná, že může vyhodit nepoužívané nody? Jasně, řešení známe, ptám se spíš proto aby sis rozmyslel, jestli to opravdu dovedeš řešit lépe než těmi sdílenými pointery.
Tak tam záleží, jak to bude navrhnuto. Ale typicky přes counter, nebo nějaký vektor. Myslím, že rychlostně by si člověk mohl pomoct.
Typický GC má "množstevní slevu" proto, že se nestará o nedosažitelné objekty, řeší jen živé. To znamená, že čím déle vydrží neběžet, tím víc bude těch mrtvých a tím víc na tom ušetří. V důsledku toho je velmi levné dělat krátce žijící objekty, což má spoustu dalších pozitivních důsledků. Naopak new/delete se poctivě stará o každý objekt, což je dost neefektivní a v důsledku nutí programátora, aby se krátkodobým objektům vyhnul.
Jak se stará jen o živé? Ten overhead u GC nevadí? A proč by v C++ mělo být velmi drahé dělat krátce žijící objekty? Copak si nemůžu naalokovat paměť, kterou pak budu předávat, abych nemusel pořád alokovat a dealokovat?
Kdysi jsem si potřeboval ověřit, jak moc v javě můžu prasit a bezohledně alokovat nové a vyšlo mi, že má asi desetinásobnou průchodnost proti MS C++. Čili za daný čas dokáže alokovat a zahodit 10x víc objektů než C++.
Tak zrovna MS C++ není úplně vypovídající. Také by mě zajímalo, jak jsi to měřil. :)
Pro lowlevel benchmark jsem si prostě udělal pole řádově tisíce pointerů a do toho jsem alokoval stále nové objekty. Ty něco triviálního počítaly a pak je přeplácnul nový objekt. Tohle samozřejmě GC chutná nejvíc, takže se tam projeví výhody proti delete.
To je teda hodně debilní benchmark.
-
Ano, můžeš zkoušet různé countery, vektory, předalokace, dealokace a věřit, že tím obejdeš nevýhody new/delete. Však si to někdy zkus. Můj první příspěvek byl právě o tom, že GC je v tomhle přinejmenším dobrý kompromis vzhledem k tomu, jak to usnadňuje práci programátora. Ano, namítáš, že C++ programátor si může vybrat, já říkám, že výkon tohoto mechanismu je tak mizerný, že u složitějších problémů je k tomu donucen.
Já si to zkoušel, ale přes unique_ptr. A nenamítám, že programátor v C++ si může vybrat, namítám, že GC není jednoznačnou výhodou a že ne-GC přístup má své nesporné výhody. Navíc nejprve by to chtělo specifikovat, co si představuješ pod pojmem "výkon".
GC má samozřejmě overhead, ale když na něj dojde, tak často existuje jen zlomek objektů, co prošly tím programem. Pořeší tento zlomek a je hotovo. Delete se poctivě stará o každých pár byte co si někde alokuješ, to je prostě ohromný rozdíl.
Dělal jsem spoustu měření na různě složitých algoritmech. Pro lowlevel benchmark jsem si prostě udělal pole řádově tisíce pointerů a do toho jsem alokoval stále nové objekty. Ty něco triviálního počítaly a pak je přeplácnul nový objekt. Tohle samozřejmě GC chutná nejvíc, takže se tam projeví výhody proti delete.
Obávám se, že tohle nechápu. Zaprvé pokud existuje zlomek objektů, co prošly programem, tak proč bych měl v ne-GC přístupu alokovat víc? Zadruhé ten GC se taky stará o každý byte, protože nechce mít memory-leaky (a ten delete volá taky) - hlavní rozdíl je "kdy" a "jak často".
Ad ten příklad - pokud sis udělal řádově tisíce pointerů a do nich alokoval stále nové objekty, které něco počítaly a pak je přeplácnul, tak nechápu, proč jsi volal delete. Delete voláš až na konci, proč bych dealokoval paměť, kterou budu vzápětí alokovat? To potom chápu, že to pro GC vyházelo lépe, ale v takovém případě je problém jinde... :)
Účelem toho benchmarku bylo simulovat alokace v běžném programu. Tam jsou různé třídy a dělají různé věci. Tedy dává smysl alokovat různé objekty, k něčemu je použít a zase je smazat. Pokud bych to dělal jinak, tak bych netestoval to co jsem testovat chtěl.
Když už se pouštíš do debaty o GC, tak předpokládám, že aspoň zhruba víš jak funguje. Pro představu co se běžně používá - je kus paměti vyhrazen pro nové objekty, když tam dochází místo, tak živé (ty na které existuje odkaz) se vykopírují jinam a paměť se může znovu použít. Všimni si, že jsem vůbec nezmínil mrtvé objekty, pro ty se totiž nemusí dělat vůbec nic, žádný delete. V běžném programu je těch mrtvých v každé iteraci třeba 95%, takže je to proti klasickému delete výrazná úspora za cenu toho, že se musí 5% dat kopírovat.
-
Pro lowlevel benchmark jsem si prostě udělal pole řádově tisíce pointerů a do toho jsem alokoval stále nové objekty. Ty něco triviálního počítaly a pak je přeplácnul nový objekt. Tohle samozřejmě GC chutná nejvíc, takže se tam projeví výhody proti delete.
To je teda hodně debilní benchmark.
Chápu, že jsi nemusel pochopit motivaci k takovému lowlevel benchmarku, ale stačí se zeptat, nemusíš tady hned vylévat své frustrace.
-
Je to přesně opačně. GraalVM je marketingové označení od Oraclu, který vzal několik souvisejících projektů a spojil je pod tímhle názvem, aby to mohl prodávat do enterprise sféry.
Fakt? ja hovorim o Community Edition. A vy? Nikde som nevidel, ze by sa nu od Oraclu dal ziskat plateny support. vy snad ano? Nehovoriac o tom, ze cielom projektov ako MLE je, ze ziaden GraalVM treba neni.
Kudne si mozete kupit aj Oracle Linux, ale neviem ako to meni fakt na tom, ze Fedora pren sluzi ako bleeding edge testbed prostredie.
-
Ad ten příklad - pokud sis udělal řádově tisíce pointerů a do nich alokoval stále nové objekty, které něco počítaly a pak je přeplácnul, tak nechápu, proč jsi volal delete. Delete voláš až na konci, proč bych dealokoval paměť, kterou budu vzápětí alokovat? To potom chápu, že to pro GC vyházelo lépe, ale v takovém případě je problém jinde... :)
Účelem toho benchmarku bylo simulovat alokace v běžném programu. Tam jsou různé třídy a dělají různé věci. Tedy dává smysl alokovat různé objekty, k něčemu je použít a zase je smazat. Pokud bych to dělal jinak, tak bych netestoval to co jsem testovat chtěl.
Když už se pouštíš do debaty o GC, tak předpokládám, že aspoň zhruba víš jak funguje. Pro představu co se běžně používá - je kus paměti vyhrazen pro nové objekty, když tam dochází místo, tak živé (ty na které existuje odkaz) se vykopírují jinam a paměť se může znovu použít. Všimni si, že jsem vůbec nezmínil mrtvé objekty, pro ty se totiž nemusí dělat vůbec nic, žádný delete. V běžném programu je těch mrtvých v každé iteraci třeba 95%, takže je to proti klasickému delete výrazná úspora za cenu toho, že se musí 5% dat kopírovat.
Cikáda totiž neustále recykluje již alokované objekty, což je u strukturovaného programování běžnou záležitostí. GC by v takovém případě nebyl nikdy aktivován, benchmark by nezaznamenal rozdíl.
Významný rozdíl však zjistíme při požadavku na immutable objekty, což je zejména u vícevláknových aplikací žádoucí. Zde bude mít GC výkonově jasně navrch za cenu občasných latencí.
-
Fakt?
Ano.
ja hovorim o Community Edition. A vy?
Já píšu o GraalVM EE. Ptal jste se na enterprise nasazení.
Nikde som nevidel, ze by sa nu od Oraclu dal ziskat plateny support. vy snad ano?
Tak proč diskutujete o něčem, o čem nic nevíte? Ve středu byla oznámena GraalVM 19.0 (https://medium.com/graalvm/announcing-graalvm-19-4590cf354df8), první produkční verze GraalVM. Vydána byla ve dvou edicích, jak bylo celou dobu plánováno, v edice Community Edition a v edici Enterprise Edition. Na Enterprise Editionposkytuje Oracle komerční podporu.
Nehovoriac o tom, ze cielom projektov ako MLE je, ze ziaden GraalVM treba neni.
Nenašel jsem, co je projekt MLE. GraalVM je virtuální stroj nové generace, podporuje běh více jazyků, AOT kompilaci do nativního kódu – mně osobně to připadá dost užitečné, nejen pro Javu.
-
ja hovorim o Community Edition. A vy?
Já píšu o GraalVM EE. Ptal jste se na enterprise nasazení.
Ja som sa konkretne vas na ziadne enterprise nasadenie nepytal. Napisal som, ze GraalVM sluzi ako testbed, aby sa technologie dostali co najskor medzi developerov.
Nenašel jsem, co je projekt MLE. GraalVM je virtuální stroj nové generace, podporuje běh více jazyků, AOT kompilaci do nativního kódu – mně osobně to připadá dost užitečné, nejen pro Javu.
Vy ste co? Predavac s teplou vodou od Oracle?
Nechcem vam kazit den, ale ziadna generacna zmena sa v GraalVM nekona. Je to klasicky 8ckovy HotSpot rozsireny o JVMCI. JVMCI je sucastou OpenJDK od 9ny. O Jave 9, 10, 11 aj 12 preco potom nepisete, ze je to "vm novej generacie"? Tie si to nezasluzia?
-
Ano, můžeš zkoušet různé countery, vektory, předalokace, dealokace a věřit, že tím obejdeš nevýhody new/delete. Však si to někdy zkus. Můj první příspěvek byl právě o tom, že GC je v tomhle přinejmenším dobrý kompromis vzhledem k tomu, jak to usnadňuje práci programátora. Ano, namítáš, že C++ programátor si může vybrat, já říkám, že výkon tohoto mechanismu je tak mizerný, že u složitějších problémů je k tomu donucen.
Já si to zkoušel, ale přes unique_ptr. A nenamítám, že programátor v C++ si může vybrat, namítám, že GC není jednoznačnou výhodou a že ne-GC přístup má své nesporné výhody. Navíc nejprve by to chtělo specifikovat, co si představuješ pod pojmem "výkon".
GC má samozřejmě overhead, ale když na něj dojde, tak často existuje jen zlomek objektů, co prošly tím programem. Pořeší tento zlomek a je hotovo. Delete se poctivě stará o každých pár byte co si někde alokuješ, to je prostě ohromný rozdíl.
Dělal jsem spoustu měření na různě složitých algoritmech. Pro lowlevel benchmark jsem si prostě udělal pole řádově tisíce pointerů a do toho jsem alokoval stále nové objekty. Ty něco triviálního počítaly a pak je přeplácnul nový objekt. Tohle samozřejmě GC chutná nejvíc, takže se tam projeví výhody proti delete.
Obávám se, že tohle nechápu. Zaprvé pokud existuje zlomek objektů, co prošly programem, tak proč bych měl v ne-GC přístupu alokovat víc? Zadruhé ten GC se taky stará o každý byte, protože nechce mít memory-leaky (a ten delete volá taky) - hlavní rozdíl je "kdy" a "jak často".
Ad ten příklad - pokud sis udělal řádově tisíce pointerů a do nich alokoval stále nové objekty, které něco počítaly a pak je přeplácnul, tak nechápu, proč jsi volal delete. Delete voláš až na konci, proč bych dealokoval paměť, kterou budu vzápětí alokovat? To potom chápu, že to pro GC vyházelo lépe, ale v takovém případě je problém jinde... :)
Účelem toho benchmarku bylo simulovat alokace v běžném programu. Tam jsou různé třídy a dělají různé věci. Tedy dává smysl alokovat různé objekty, k něčemu je použít a zase je smazat. Pokud bych to dělal jinak, tak bych netestoval to co jsem testovat chtěl.
Když už se pouštíš do debaty o GC, tak předpokládám, že aspoň zhruba víš jak funguje. Pro představu co se běžně používá - je kus paměti vyhrazen pro nové objekty, když tam dochází místo, tak živé (ty na které existuje odkaz) se vykopírují jinam a paměť se může znovu použít. Všimni si, že jsem vůbec nezmínil mrtvé objekty, pro ty se totiž nemusí dělat vůbec nic, žádný delete. V běžném programu je těch mrtvých v každé iteraci třeba 95%, takže je to proti klasickému delete výrazná úspora za cenu toho, že se musí 5% dat kopírovat.
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.
-
Ja som sa konkretne vas na ziadne enterprise nasadenie nepytal.
Máte pravdu, nebyla to otázka. Bylo to tvrzení, které bylo fakticky špatně. Proto jsem ho opravoval. Konkrétně jste napsal: „GraalVM nebude na enterprise nasadenie ready nikdy.“
Napisal som, ze GraalVM sluzi ako testbed, aby sa technologie dostali co najskor medzi developerov.
Ano, to jste napsal, a už jsem vám to vyvracel, že jste to napsal špatně. Vývojáři používají jednotlivé technologie (SubstrateVM, native-image, Truffle), GraalVM je naopak marketingový název Oraclu, aby to mohl propagovat jako balík a nabídnout k tomu placený support.
Vy ste co? Predavac s teplou vodou od Oracle?
Nikoli, pouze vyvraceč nesprávných tvrzení.
Nechcem vam kazit den, ale ziadna generacna zmena sa v GraalVM nekona. Je to klasicky 8ckovy HotSpot rozsireny o JVMCI. JVMCI je sucastou OpenJDK od 9ny. O Jave 9, 10, 11 aj 12 preco potom nepisete, ze je to "vm novej generacie"? Tie si to nezasluzia?
Jedna z komponent GraalVM je SubstrateVM, což je JVM s AOT kompilací (na rozdíl od HotSpotu, který má JIT kompilaci). Součástí GraalVM je je úplně nový kompilátor z bajtkódu do nativního kódu, je napsaný v Javě a umí ho používat jak HotSpot tak SubstrateVM. Pod GraalVM také patří Truffle, který umožňuje přidávat do VM podporu dalších jazyků (vedle Java bytecode). GraalVM pak umožňuje i současný běh různých jazyků pod jednou VM, takže můžete aplikaci napsanou v Javě nakonfigurovat v JavaScriptu a pak spustit nějaký výpočet v R. Pro Truffle existuje i implementace pro LLVM bajtkód, takže pod GraalVM můžete spouštět i programy napsané v některém z jazyků, které podporuje LLVM.
To všechno jsou věci, o kterých se původnímu HotSpotu ani nesnilo. Že je to vše udělané tak, aby se to dalo používat z původního HotSpotu, je plus, pomáhá to překonat problémy se zpětnou kompatibilitou.
-
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.
-
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.
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é.
-
No tak jsem zvedavy jak to s tim GraalVM dopadne. Ja bych byl v soucasnosti mohem radeji, kdyby v Jave slo konecne nejak normalne pouzivat Await Async. Protoze to co umi CompletableFuture nahrada neni ani nahodou, uz treba proto, ze clovek u toho musi resit problemy se soubehem vicero vlaken. To by se u normalniho stavoveho stroje s await async nemelo stat, protoze tam bezi jenom jeden thread. Sice existuje ten projekt Quasar, ale kdyz jsem se dival na ukozakovy kod, tak jsem moc nadseny nebyl. Chtel bych neco jednoducheho, kde muzu udelat:
CompletableFuture.supplyAsync(() -> doSomething());
A cus. Protoze na tom, jak se neco pouziva a jak slozite je to nakonfigurovat, setsakramentsky zalezi. To ma problem pochopit Jirsak. Protoze pokud se neco nepouziva dobre, tak to v praxi pro 9 z 10 javistu v enterprise znamena, ze se k tomu nikdy nedostane, protoze ta technologie pak do enterprise enviromentu nikdy neprobubla.
Java proste doted await async neumi a je to podle me velka ostuda. My co delame v enterprise tak nevim jestli nam k necemu je GraalVM, ale sakra bych vyuzili v nasich komponentach await async. Ale to proste je techbologie, ktera musi byt pripravena a musi se dat pouzit lusknutum prstu.
-
Nicmene co me tesi na GraalVM a kompilaci do nativniho kodu je to, ze to zmeni design Javovskych frameworku k lepsimu. Ale to potrva leta. Protoze ta kompilaci na native ma nejake problemy s reflexi, a ja si myslim, ze Spring pouziva reflexi az presprilis. Takze treba ten Micronaut je v tomhle vice odlehceny a mozna je to castecne zasluha toho GraalVM. Akorat nevim jak se tam treba resi anotace typu @Transactional, asi nijak. Snad se jednou dockam jazyka, kde se @Transactonal a podobne veci poresi uz v prubehu kompilace, protoze se mi nelibi, kdyz se porad vsechno musi volat pres proxy beany.
-
Vidím, že se specializujete na komentování věcí, o kterých nic nevíte.
ze to zmeni design Javovskych frameworku k lepsimu.
Je pozoruhodné, jak často na Javu nadávají lidé, kteří předpokládají, že Java == Spring.
Protoze ta kompilaci na native ma nejake problemy s reflexi
Ve skutečnosti se akorát musí pro reflexi při kompilaci přibalit další informace. Nedivil bych se, kdyby to v budoucnosti nebylo potřeba. Ostatně kdyby se spojila AOT a JIT kompilace (aplikace by nastartovala z nativního kódu, ale dál by se v při běhu uměla optimalizovat pomocí JIT), byl by to další krok vpřed.
ja si myslim, ze Spring pouziva reflexi az presprilis
Podle mne Spring vůbec nesmyslně vše řeší až za běhu, přitom většina věcí, co dělá Spring, se dá řešit v době kompilace. Podle mne málokdo používá modularitu až v době běhu. Ale vzhledem k době vzniku je to pochopitelné.
Takze treba ten Micronaut je v tomhle vice odlehceny a mozna je to castecne zasluha toho GraalVM.
Nemyslím si, že je to zásluha GraalVM. Podpora pro GraalVM byla do Micronautu dodána až později. Udělat něco jako Spring, ale anotace použít tak, jak byly původně především zamýšlené – tj. využít je při překladu – vyselo ve vzduchu, byla jen otázka času, kdy se do toho někdo pustí.
Akorat nevim jak se tam treba resi anotace typu @Transactional, asi nijak. Snad se jednou dockam jazyka, kde se @Transactonal a podobne veci poresi uz v prubehu kompilace, protoze se mi nelibi, kdyz se porad vsechno musi volat pres proxy beany.
Micronaut anotaci @Transactional umí používat a řeší ji přesně tak, jako řeší vše ostatní – už v průběhu kompilace.
To by se u normalniho stavoveho stroje s await async nemelo stat, protoze tam bezi jenom jeden thread.
Jistě, použít jediné vlákno je na dnešních mnohojádrových procesorech výborný nápad.
Protoze na tom, jak se neco pouziva a jak slozite je to nakonfigurovat, setsakramentsky zalezi. To ma problem pochopit Jirsak.
Naopak, já tohle chápu velice dobře. Mimochodem, v tom je právě jedna z předností Javy, že je prostředí kolem už tak bohaté, že se spousta i poměrně složitých věcí používá velmi jednoduše. GraalVM je mladá technologie, takže to samozřejmě bude chvíli trvat, než bude také tak snadno použitelná, ale to je problém všech nových technologií, včetně třeba toho Go. Navíc Oracle se dost snaží GraalVM nedělat jako revoluci ale jako evoluci současných nástrojů, takže některé vlastnosti můžete používat tak, že OpenJDK spustíte s určitým parametrem a do projektu si přidáte závislost na normálním jarku dostupném v Maven repository. Vás to dokonce zmátlo tak, že jste si myslel, že to ani nepřináší podstatné nové možnosti.
-
To by se u normalniho stavoveho stroje s await async nemelo stat, protoze tam bezi jenom jeden thread.
Jistě, použít jediné vlákno je na dnešních mnohojádrových procesorech výborný nápad.
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.
Dalsi vec i kdybyste mel nejakou singleuser komponentu, tak pokud pokud jsou hlavnim problemem blokujici operace, tak na to zase nechcete pouzivat vlakna, akorat si budete komplikovat zivot. Takze v Jave nahrada za await async neni. Resp. je, ve Springu "@Async" a nakonfigurovat to at se berou vlakna z Quasar, ale to je zase tak pres koleno...
-
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.
Není to nesmysl, běžně se dělá to, že pro zpracování síťové komunikace (přijetí požadavku) se používá jeden pool vláken, a pro vlastní zpracování požadavku (výpočet, dotaz do databáze, odeslání jiného požadavku) jiný pool vláken.
Dalsi vec i kdybyste mel nejakou singleuser komponentu, tak pokud pokud jsou hlavnim problemem blokujici operace, tak na to zase nechcete pouzivat vlakna, akorat si budete komplikovat zivot. Takze v Jave nahrada za await async neni. Resp. je, ve Springu "@Async" a nakonfigurovat to at se berou vlakna z Quasar, ale to je zase tak pres koleno...
To je těžké, když jediné, co znáte, je Spring. Ve standardní knihovně máte concurrent framework, máte RxJava nebo Reactor, Netty (a tím pádem Micronaut) používá zmíněný pool IO vláken a pool workerů, Jetty má Continuations a tím byly inspirovány Asynchronous servlets. Přičemž Continuations jsou právě koncept await/async. Springovské @Async je něco jiného, je to klasické asynchronní zpracování, není tam to await.
-
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.
Není to nesmysl, běžně se dělá to, že pro zpracování síťové komunikace (přijetí požadavku) se používá jeden pool vláken, a pro vlastní zpracování požadavku (výpočet, dotaz do databáze, odeslání jiného požadavku) jiný pool vláken.
Dalsi vec i kdybyste mel nejakou singleuser komponentu, tak pokud pokud jsou hlavnim problemem blokujici operace, tak na to zase nechcete pouzivat vlakna, akorat si budete komplikovat zivot. Takze v Jave nahrada za await async neni. Resp. je, ve Springu "@Async" a nakonfigurovat to at se berou vlakna z Quasar, ale to je zase tak pres koleno...
To je těžké, když jediné, co znáte, je Spring. Ve standardní knihovně máte concurrent framework, máte RxJava nebo Reactor, Netty (a tím pádem Micronaut) používá zmíněný pool IO vláken a pool workerů, Jetty má Continuations a tím byly inspirovány Asynchronous servlets. Přičemž Continuations jsou právě koncept await/async. Springovské @Async je něco jiného, je to klasické asynchronní zpracování, není tam to await.
To by me zajimalo jak chcete s thready napsat aynchronni kod. 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. Jenom tak se vam bude spoustet kaskada operaci zatimco normalne pisete relativne standardni kod a nesnazite se komplikovat si design paralelnim zpracovanim.
Springovske Async neni neco jineho, kdyz se to spravne pouzije (kdyz se budou vyrabet Fibery z Quasaru), tak je to prave TO Await Async, podobne jako je v C#. Akorat je to zatizene prasackou reflexi Springu, protoze Java await async neumi. A pri blokujicich operaci to bude stejne vyrabet thread, protoze v Jave ejste neni moc standardni mit neblokujici operace, neblokujici JDBC se zacalo vyrabet snad teprve pred 1 rokem. (!!!!!!)
-
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.
-
To by me zajimalo jak chcete s thready napsat aynchronni kod.
No, ehm, úplně normálně? Asynchronní kód s vlákny dělá třeba to vaše @Async ze Springu, ostatně i primitivní new Thread().start() je asynchronní kód s vlákny.
Protoze k tomu abyste z toho opravdu dokazal tezit, tak potrebujete mit drtivou vetsinu metod await async
Asi bychom si nejprve měli ujasnit, co je await/async – je to syntaktický cukr, aby kód, který se provádí asynchronně, byl zapsaný jako klasický synchronní kód. Normálně u asynchronního volání určíte, co se má dít po do dokončení příslušného kódu. Await/async znamená, že to asynchronní volání nějak označíte (pokud má podporu přímo jazyk, obvykle klíčovým slovem await), a kód, který se má provést po dokončení asynchronního volání pak píšete pod to asynchronní volání. Ohledně možností toho, co můžete napsat, se nic nemění.
Jenom tak se vam bude spoustet kaskada operaci zatimco normalne pisete relativne standardni kod a nesnazite se komplikovat si design paralelnim zpracovanim.
Nemůžu si pomoci, ale podle mne je await špatný koncept. Ten nápad „asynchronní kód je pro programátora složitější, uděláme to, aby to vypadalo, že o žádný asynchronní kód nejde“ staví na tom, že ta komplikovanost vzniká jenom strukturováním kódu. Jenže podle mne by si programátor měl být vědom toho, že pracuje s asynchronním kódem, protože jednak to stejně může mít vedlejší efekty, jednak kód psaný neasynchronně nebude při provádění tak efektivní, jako když programátor záměrně píše asynchronní kód.
Springovske Async neni neco jineho, kdyz se to spravne pouzije (kdyz se budou vyrabet Fibery z Quasaru), tak je to prave TO Await Async, podobne jako je v C#.
Springovské @Async předá provedení kódu standardnímu javovskému Executoru. V kódu, který volá @Async metodu, dostanete jako návratovou hodnotu Future a kód normálně pokračuje v provádění, nečeká na dokončení té asynchronní metody. Pokud chcete udělat await, musíte na tom Future vzápětí zavolat get(). To už ale nijak nesouvisí se Springovským @Async, to je normální chování Future.
-
Nemůžu si pomoci, ale podle mne je await špatný koncept. Ten nápad „asynchronní kód je pro programátora složitější, uděláme to, aby to vypadalo, že o žádný asynchronní kód nejde“ staví na tom, že ta komplikovanost vzniká jenom strukturováním kódu. Jenže podle mne by si programátor měl být vědom toho, že pracuje s asynchronním kódem,
Vidím to přesně stejně. Imho pokud se má asynchronní programování nějak zjednodušovat, tak cestou explicitních greenthreadů/coroutin, kde:
1. je naprosto explicitní a zjevné, co může (potenciálně) běžet zaráz a co ne
2. programátor v klidu může použít blokování, pokud ho potřebuje ( sleep(3000) ), a je mu bezprostředně jasný jeho význam, protože se dá nad programem jako celkem relativně snadno uvažovat (to reason about)
-
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.
https://stackoverflow.com/questions/14455293/how-and-when-to-use-async-and-await
Await/async neni v C# v zadne pripade "jen" syntakticky cukr. Kdyby byl, tak jak to udelas v Jave? Nijak. Pod Await/Async je totiz v C# stavovy stroj, Java to neumi.
https://stackoverflow.com/questions/2846664/implementing-coroutines-in-java
Thread-based coroutines
This "solution" is pathological. The whole point of coroutines is to avoid the overhead of threading, locking, kernel scheduling, etc. Coroutines are supposed to be light and fast and to execute only in user space. Implementing them in terms of full-tilt threads with tight restrictions gets rid of all the advantages.
Za dalsi, neznam nic jako "jen syntakticky cukr" - vyprosuju si vypustit to "jen". Protoze to uz potom muzeme rict, ze monitor je jen syntakticky cukr pro jen programatora, protoze frajeri pouzivaji multimetr a osciloskop pro zjisteni stavu jednotlivych bitu.
-
A je jedno, ze si nekdo mysli, jak se da neco zbastlit s await async a coroutinami. Zbastlit se da totiz uplne vsechno.
Java NEUMI Coroutiny a rovnocenna nahrada za ne neexistuje. Tecka.
A neumi coroutiny proto, ze by byly uplna na hovno, kdyz Java dodneska nema neblokujici API prinejmensim k JDBC. Protoze vyzirkove z Oracle teprve minuly rok zacali vyvije standard pro asynchronni JDBC, ikdyz .NET uz to davno vsechno ma zmaknute.
-
Asi bychom si nejprve měli ujasnit, co je await/async – je to syntaktický cukr, aby kód, který se provádí asynchronně, byl zapsaný jako klasický synchronní kód. Normálně u asynchronního volání určíte, co se má dít po do dokončení příslušného kódu. Await/async znamená, že to asynchronní volání nějak označíte (pokud má podporu přímo jazyk, obvykle klíčovým slovem await), a kód, který se má provést po dokončení asynchronního volání pak píšete pod to asynchronní volání. Ohledně možností toho, co můžete napsat, se nic nemění.
Pane Jirsak, ja jsem to rekl nepresne, ale to vy taky.
V C#, Await by se dal povazovat za syntakticky cukr pro neco jako "CompletableFuture.get()" ale Async cukr uz neni. O keyword Async uz nemuzete rict, ze je to cukr pro ktery kompilator provede neco jako "CompletableFuture.supplyAsync(()->.)" Na zklade klicoveho slova Async se musi nejak vygenerovat stavovy stroj, predstavu jak funguje mam jen pribliznou.
A to je to, co Java neumi. Kotlin to umi ale je to experimentalni feature.
V Javascriptu si nejsem jisty, zda Promise predstavuje stejny mechanismus jako Coroutiny v C#.
-
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.
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.
-
V Javascriptu si nejsem jisty, zda Promise predstavuje stejny mechanismus jako Coroutiny v C#.
Je to stejné.
-
Await/async neni v C# v zadne pripade "jen" syntakticky cukr. Kdyby byl, tak jak to udelas v Jave? Nijak. Pod Await/Async je totiz v C# stavovy stroj, Java to neumi.
Pro stavový stroj nepotřebuju žádnou speciální podporu kompilátoru. Await/async jste na implementaci v C# zúžil právě teď, předtím jste psal o obecném await/async.
Zvláštní je, že když to v Javě nijak naprogramovat nejde, Jetty to má naprogramované jako Continuations (https://www.eclipse.org/jetty/). Ale to už jsme vám přece psal…
Za dalsi, neznam nic jako "jen syntakticky cukr"
Tak se s tím pojmem seznamte. Třeba v JavaScriptu je async/await v podstatě jen syntaktický cukr pro Promise.then(), liší se jen ve zpracování chybových stavů.
V C#, Await by se dal povazovat za syntakticky cukr pro neco jako "CompletableFuture.get()" ale Async cukr uz neni. O keyword Async uz nemuzete rict, ze je to cukr pro ktery kompilator provede neco jako "CompletableFuture.supplyAsync(()->.)" Na zklade klicoveho slova Async se musi nejak vygenerovat stavovy stroj, predstavu jak funguje mam jen pribliznou.
No jo, jenže vy nepíšete o async/await, ale o korutinách.
-
Java NEUMI Coroutiny a rovnocenna nahrada za ne neexistuje. Tecka.
Chápu. Takže vy píšete spoustu nesmyslů, a když konečně napíšete něco správně, napíšete to tučně, abyste to od těch nesmyslů odlišil. Takže si ty nesmysly řešte s někým jiným, já si počkám, až za dvě stránky zase dospějete k něčemu pravdivému a napíšete to tučně.
-
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.
-
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.
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).
-
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.
Jenže v tom JS to přenechání vlákna neplyne z async/await, ale z promise a z interní implementace toho blokujícího volání. V JS je async/await prakticky opravdu jen syntaktický cukr. Když se vykašlete na ošetření chyb, můžete v JS async/await přepsat na Promise.then() a nebude v tom žádný rozdíl.
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.
V JavaScriptu (předpokládám, že je řeč stále o něm) má Promise dva kanály – then pro validní výsledek a catch pro chybu. Takže prosté probublání chyby až na konec je tam také jednoduché. Složitější může být, pokud se někde v tom zřetězení callbacků chcete z chyby zotavit, případně je snazší tam výjimku úplně zazdít.
-
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ý.
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.
-
.
-
Java také nebude ten ideální jazyk... 8)
public class HelloWorld
{
public static void main(String[] args)
{
Integer a = 1024;
Integer b = 1024;
System.out.print(a == b); // false
}
}
-
Java také nebude ten ideální jazyk... 8)
public class HelloWorld
{
public static void main(String[] args)
{
Integer a = 1024;
Integer b = 1024;
System.out.print(a == b); // false
}
}
Vzhledem k tomu, že programovací jazyk je nástroj pro programátory, asi by ideální programovací jazyk neměli vybírat neprogramátoři.
-
Java také nebude ten ideální jazyk... 8)
public class HelloWorld
{
public static void main(String[] args)
{
Integer a = 1024;
Integer b = 1024;
System.out.print(a == b); // false
}
}
Vzhledem k tomu, že programovací jazyk je nástroj pro programátory, asi by ideální programovací jazyk neměli vybírat neprogramátoři.
"Idealni" znamena "idealni pro programatory"? To je podle me malo. Videl jsem uz spoustu java projektu, ale nevzpomenu si ani na jeden kde by jazyk byl vybran programatory.
Rozsirenost javy je podle me zpusobena tim, ze je zdanlive jednoducha a proto lze na trhu najit dostupne vyvojare a dostatek klientu, kteri si nechaji nabulikovat, ze je to dobry napad.
PS: Se sterkou na Johnyho souhlasim, protoze to byl fakt debilni argument.
-
"Idealni" znamena "idealni pro programatory"? To je podle me malo. Videl jsem uz spoustu java projektu, ale nevzpomenu si ani na jeden kde by jazyk byl vybran programatory.
Rozsirenost javy je podle me zpusobena tim, ze je zdanlive jednoducha a proto lze na trhu najit dostupne vyvojare a dostatek klientu, kteri si nechaji nabulikovat, ze je to dobry napad.
PS: Se sterkou na Johnyho souhlasim, protoze to byl fakt debilni argument.
Souhlasím, že o výběru programovacího jazyka nerozhodují jen programátoři. Ale vzhledem k tomu, na co jsem reagoval, mi připadalo zbytečné zabíhat do detailů…
-
Java také nebude ten ideální jazyk... 8)
public class HelloWorld
{
public static void main(String[] args)
{
Integer a = 1024;
Integer b = 1024;
System.out.print(a == b); // false
}
}
Franta má 1024 Kč, Pepa má také 1024 Kč. Pepa je Franta.
-
Franta má 1024 Kč, Pepa má také 1024 Kč. Pepa je Franta.
No jenže pokud ale mají oba pouze 50 Kč, pak opravdu jo:
public class HelloWorld
{
public static void main(String[] args)
{
Integer a = 50;
Integer b = 50;
System.out.print(a == b); // true
}
}
-
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.
-
A když to budou tuny zlata? :o
-
Franta má 1024 Kč, Pepa má také 1024 Kč. Pepa je Franta.
No jenže pokud ale mají oba pouze 50 Kč, pak opravdu jo:
public class HelloWorld
{
public static void main(String[] args)
{
Integer a = 50;
Integer b = 50;
System.out.print(a == b); // true
}
}
Nemá smysl se vrtat v těchle profláklejch kravinkách. Každej jazyk a platforma co má něco za sebou obsahuje takovýhle věci, protože co se zdálo dobré tenkrát se za 10 - 20 let ukáže jako špatné rozhodnutí. Java si tím prošla několikrát a už má nasbíráno pěknou řádku kostlivců, na druhou stranu pořád to není tak hrozný právě kvůli její jednoduchosti. Podívej se na specifikaci rovnosti v javascriptu, pochybuju, že řadový JS programátor by to dal dohromady. Podívej se na chytáky v C#, tam to chvíli trvalo, než třeba lambdy udělali blbuvzdorný.
Na jednu stranu můžeme jakožto líní programátoři žádat, aby byl jazyk jednoduchý a bez nástrah, na druhou stranu to vždy bude něco za něco a holt by zatím člověk neměl otevřít editor dokud si nenačte jak se chová rovnost, spojování stringů, přetypování na bool, hashe a equals apod. To jsou věci, který jsou snad v každym jazyku jinak a dost často nějak blbě.
-
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ů.
-
Java také nebude ten ideální jazyk... 8)
public class HelloWorld
{
public static void main(String[] args)
{
Integer a = 1024;
Integer b = 1024;
System.out.print(a == b); // false
}
}
Franta má 1024 Kč, Pepa má také 1024 Kč. Pepa je Franta.
Což ten kód neříká, že áno. V tom je ten podraz.
-
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ů.
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ždyť jo - pokud má Pepa a Franta jenom 50Kč, tak jsou to socky a pro javisty je socka jako socka.
:D
-
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ů.
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...
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.
-
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ů.
Na upozornění na kontrainutitivnost nějakého prvku jazyka není nic hloupého.
-
Java také nebude ten ideální jazyk... 8)
public class HelloWorld
{
public static void main(String[] args)
{
Integer a = 1024;
Integer b = 1024;
System.out.print(a == b); // false
}
}
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.
-
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.
-
Java také nebude ten ideální jazyk... 8)
public class HelloWorld
{
public static void main(String[] args)
{
Integer a = 1024;
Integer b = 1024;
System.out.print(a == b); // false
}
}
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 je v Javě objekt (na rozdíl od int). A k objektům se přistupuje vždy odkazem a nikoliv hodnotou. Tudíž výraz a == b neporovnává dvě shodná čísla, ale dva různé odkazy na stejné číslo.
-
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...
Vážně byste si měl nastudovat rozdíl mezi hodnotou a referencí. Představte si, že máte na tabuli napsaná čísla, a pak tam přidáváte šipky, které na ta čísla ukazují. Na jedno číslo může ukazovat víc šipek. To, že máte v ruce dvě různé šipky, neznamená, že obě šipky nemohou ukazovat na stejné číslo. Pokud chcete porovnávat čísla, musíte se dívat, kam šipka ukazuje, ne na šipku samotnou.
-
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...
Vážně byste si měl nastudovat rozdíl mezi hodnotou a referencí. Představte si, že máte na tabuli napsaná čísla, a pak tam přidáváte šipky, které na ta čísla ukazují. Na jedno číslo může ukazovat víc šipek. To, že máte v ruce dvě různé šipky, neznamená, že obě šipky nemohou ukazovat na stejné číslo. Pokud chcete porovnávat čísla, musíte se dívat, kam šipka ukazuje, ne na šipku samotnou.
A vo tom to není, že áno. Nepochybuji o tom, že rozdílu mezi hodnotou a referencí moc dobře rozumí.
-
Hloupé je to, že uvádí jeden okrajový příklad, a zdá se, že vůbec nechápe podstatu.
Porovnani dvou cisel mi neprijde nijak okrajove. A jestli chape nebo nechape podstatu, 1. neumim z niceho odvodit, 2. je to irelevantni - porad je ta vec kontraintuitivni.
To, že operátor == v Javě porovnává hodnoty primitivních typů a reference u objektů mi nijak neintuitivní nepřipadá.
Je to kontraintuitivni - jestlize o Jave nic nevim, muzu si rict tak leda "WTF" - vubec me nenapadne, proc by se to jednou melo chovat tak a jindy jinak.
Uz to samotne vase "jenom pro větší čísla" ukazuje na neco, co vypada divne. "Vetsi cisla"? To je co? Vime to? Je to nekde v dokumentaci? Nebo se to chova "nahodne" (podle toho, jak velky je zrovna ten pool)?
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.
Tak dalsi mozne reseni by bylo (pokud jsem spravne pochopil princip) nemit ten pool pro "mensi cisla"?
Jinak existuje urcite aspon pet, ne-li vic moznych jinych reseni, ale nektere z nich by vyzadovaly nejake featury, ktere aktualne Java asi nema.
-
Tak dalsi mozne reseni by bylo (pokud jsem spravne pochopil princip) nemit ten pool pro "mensi cisla"?
...anebo ho naopak mit povinne (tak to ma treba Erlang s atomy - zarucene vzdycky jsou stejne atomy "unifikovane"). Proste aby se to chovalo tak nebo tak, ale konzistentne, predvidatelne.
-
Integer v Javě je objekt, operátor == v Javě porovnává reference...
Dík, zmátlo mě to cachování malých integerů. Ani z toho, že == má porovnávat reference jsem nebyl schopný vydedukovat, co se tam děje.
To, že operátor == v Javě porovnává hodnoty primitivních typů a reference u objektů mi nijak neintuitivní nepřipadá.
Rozhodně je to nekonzistentní. že budou vestavěné typy a objekty podporovat jiné operace bych bral, že ale stejná operace funguje na obou jinak mi přijde jako past na nováčky.
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.
Intuitivní by mi přišlo mít dvě porovnání, jedno na identitu a jedno na hodnotu, a nechat je chovat se konzistentně pro všechno. Samotná syntaxe těch dvou porovnání už je vlastně detail.
-
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.
Lepší by pochopitelně bylo přetížení operátoru ==.
-
Porovnani dvou cisel mi neprijde nijak okrajove. A jestli chape nebo nechape podstatu, 1. neumim z niceho odvodit, 2. je to irelevantni - porad je ta vec kontraintuitivni.
Jenže on neporovnává dvě čísla, ale dvě reference.
Je to kontraintuitivni - jestlize o Jave nic nevim, muzu si rict tak leda "WTF" - vubec me nenapadne, proc by se to jednou melo chovat tak a jindy jinak.
Ano, můžeme navrhnout jazyk, který nebude mít žádné operátory, protože operátory jsou vždy v nějakém případě kontraintuitivní. Nejsem si úplně jistý, jestli by takový jazyk byl přijat s nadšením.
Uz to samotne vase "jenom pro větší čísla" ukazuje na neco, co vypada divne. "Vetsi cisla"? To je co? Vime to? Je to nekde v dokumentaci? Nebo se to chova "nahodne" (podle toho, jak velky je zrovna ten pool)?
Je to v dokumentaci. Ale je to implementační detail. Programátor klidně může zavolat new Integer(1) a vytvoří se nová instance. Pokud chci porovnávat hodnoty, musím použít metodu equals(). Je to úplně stejné, jako u Stringů. Prostě se programátor nemůže spoléhat na to, že někdy na stejnou hodnotu ukazují stejné reference.
Tak dalsi mozne reseni by bylo (pokud jsem spravne pochopil princip) nemit ten pool pro "mensi cisla"?
Nebylo. Pořád to jsou reference.
Jinak existuje urcite aspon pet, ne-li vic moznych jinych reseni, ale nektere z nich by vyzadovaly nejake featury, ktere aktualne Java asi nema.
Pět lepších řešení? Napište alespoň jedno.
-
Lepší by pochopitelně bylo přetížení operátoru ==.
Bylo by to lepší jen při pohledu z rychlíku. Myslím, že programátor například neočekává, že mu při použití operátoru vypadne výjimka.
Některé jazyky umožňují přetěžovat operátory, což pak vede k tomu, že programátor nikdy neví, co se při použití operátoru stane. Java byla navržena jako jednoduchá, takže tohle záměrně neumožnila – a jediné přetížené (ve smyslu „implementované kódem v metodách“) operátory v Javě jsou ty pracující se Stringy. Přičemž samotný objekt String je v Javě final a má speciální podporu kompilátoru. Nebo-li autoři specifikace mohli zaručit, že se metody Stringu implementující operátory budou chovat slušně a operátory nebudou mít žádné nečekané efekty. Pokud byste operátor == přetížil jako volání equals() (a třeba i ošetřil null hodnoty), pořád bude použití toho operátoru volat uživatelský kód, který může vyhazovat výjimky, dělat dotaz do databáze, equals() ani nemusí být komutativní.
Přetížený operátor == by teprve byl pořádně kontraintuitivní.
-
Lepší by pochopitelně bylo přetížení operátoru ==.
Bylo by to lepší jen při pohledu z rychlíku. Myslím, že programátor například neočekává, že mu při použití operátoru vypadne výjimka.
Některé jazyky umožňují přetěžovat operátory, což pak vede k tomu, že programátor nikdy neví, co se při použití operátoru stane. Java byla navržena jako jednoduchá, takže tohle záměrně neumožnila – a jediné přetížené (ve smyslu „implementované kódem v metodách“) operátory v Javě jsou ty pracující se Stringy. Přičemž samotný objekt String je v Javě final a má speciální podporu kompilátoru. Nebo-li autoři specifikace mohli zaručit, že se metody Stringu implementující operátory budou chovat slušně a operátory nebudou mít žádné nečekané efekty. Pokud byste operátor == přetížil jako volání equals() (a třeba i ošetřil null hodnoty), pořád bude použití toho operátoru volat uživatelský kód, který může vyhazovat výjimky, dělat dotaz do databáze, equals() ani nemusí být komutativní.
Přetížený operátor == by teprve byl pořádně kontraintuitivní.
Tenhle problém ale není omezený jenom na operátory. V jazyce, který umožňuje přetěžovat operátory, může prase napsat třeba operátor +, který nic nesčítá. V jazyce bez přetěžování napíše stejné prase funkci nebo metodu "add", která bude matoucí úplně stejně. Zákaz přetěžování operátorů tu nepomůže.
-
Rozhodně je to nekonzistentní. že budou vestavěné typy a objekty podporovat jiné operace bych bral, že ale stejná operace funguje na obou jinak mi přijde jako past na nováčky.
To je jedna z těch variant, o které jsem psal – nepovolit operátor == pro reference. Ano, dávalo by to smysl, ovšem těžko to mohlo projít v době vzniku Javy. Tenkrát mnohým připadalo jako přílišné zjednodušení i to, že Java nepodporuje přetěžování operátorů. A kdybyste jim vzal i == (pro reference)…
Jinak nejde o vestavěné typy ale o primitivní typy. String je také vestavěný typ, ale je to objekt.
Intuitivní by mi přišlo mít dvě porovnání, jedno na identitu a jedno na hodnotu, a nechat je chovat se konzistentně pro všechno. Samotná syntaxe těch dvou porovnání už je vlastně detail.
Pro primitivní typy není rozdíl mezi identitou a hodnotou.
Ve skutečnosti je problémem Javy existence primitivních typů, všechno ostatní, o čem píšeme, jsou až důsledky. Jenže to víme dnes – v době vzniku Javy byly primitivní typy považované za nezbytné, aby aplikace vůbec mohla mít nějaký výkon. Taky se tenkrát nepočítalo s tím, že JVM bude provádět nějaké velké optimalizace – takže to, že by JVM interně objektový integer nahradila jednoduchým typem bylo z říše snů. Primitivní typy v Javě byly už tenkrát vnímány jako ústupek, ale byl to ústupek tomu, aby (tehdy) Java byla vůbec nějak použitelná.
-
Tenhle problém ale není omezený jenom na operátory. V jazyce, který umožňuje přetěžovat operátory, může prase napsat třeba operátor +, který nic nesčítá. V jazyce bez přetěžování napíše stejné prase funkci nebo metodu "add", která bude matoucí úplně stejně. Zákaz přetěžování operátorů tu nepomůže.
Operátory programátor vnímá jako součást jazyka a očekává od nich, že budou dělat to, co se od nich intuitivně čeká, a že se budou chovat slušně. I když C++ programátoři pravděpodobně čekají čertoviny i od operátorů. U metod naopak každý počítá s tím, že je to uživatelsky implementovaný kód a může dělat cokoli.
-
Lepší by pochopitelně bylo přetížení operátoru ==.
Nesouhlasím. Ale ano, potom bychom se dostali do situace, že by se to Javě omlacovalo o hlavu jako třeba JavaScriptu. :)
-
Tenhle problém ale není omezený jenom na operátory. V jazyce, který umožňuje přetěžovat operátory, může prase napsat třeba operátor +, který nic nesčítá. V jazyce bez přetěžování napíše stejné prase funkci nebo metodu "add", která bude matoucí úplně stejně. Zákaz přetěžování operátorů tu nepomůže.
Operátory programátor vnímá jako součást jazyka a očekává od nich, že budou dělat to, co se od nich intuitivně čeká, a že se budou chovat slušně. I když C++ programátoři pravděpodobně čekají čertoviny i od operátorů. U metod naopak každý počítá s tím, že je to uživatelsky implementovaný kód a může dělat cokoli.
Tak tady bych silně nesouhlasil. Jsem C++ programátor a od operátorů očekávám, že čertoviny dělat nebudou. A taky ve všech knihovnách, které používám, žádné čertoviny nedělají. Úplně stejně očekávám od metod, že budou dělat to, co vyplývá z jejich jména. Ano, bugy se vyskytují, ale to i v součástech jazyka, takže tam rozdíl není.
Opravdu Javisti automaticky počítají s tím, že uživatelsky implementovaný kód může dělat cokoliv? Na takovéhle prasení vážně nejsem zvyklý a celkem mě překvapuje.
-
To, že operátor == v Javě porovnává hodnoty primitivních typů a reference u objektů mi nijak neintuitivní nepřipadá.
Je to kontraintuitivni - jestlize o Jave nic nevim, muzu si rict tak leda "WTF" - vubec me nenapadne, proc by se to jednou melo chovat tak a jindy jinak.
Uz to samotne vase "jenom pro větší čísla" ukazuje na neco, co vypada divne. "Vetsi cisla"? To je co? Vime to? Je to nekde v dokumentaci? Nebo se to chova "nahodne" (podle toho, jak velky je zrovna ten pool)?
Kdo o Javě nic neví, tak musí pochopit, že == na objektech porovnává reference. A že pokud porovnává objekty, které neudělal, tak je to implementačně závislá věc. Ten efekt se může dít u jakékoli knihovny apod.
Myslím, že u jakékoli jiné implementace toho operátoru bych musel napsat mnohem víc, abych vysvětlil jeho chování. A že takové jazyky máme a jsou i rozšířené :-)
-
Lepší by pochopitelně bylo přetížení operátoru ==.
Nesouhlasím. Ale ano, potom bychom se dostali do situace, že by se to Javě omlacovalo o hlavu jako třeba JavaScriptu. :)
JS pretezovani operatoru take neumoznuje.
-
Tak tady bych silně nesouhlasil. Jsem C++ programátor a od operátorů očekávám, že čertoviny dělat nebudou. A taky ve všech knihovnách, které používám, žádné čertoviny nedělají.
Třeba to, že se operátor > používal pro zápis do streamu (což je blokující operace, navíc může vyhodit výjimku), tu už se v C++ nedělá?
Úplně stejně očekávám od metod, že budou dělat to, co vyplývá z jejich jména.
To já také. Jenže u spousty typů je tolik různých variant, co může dělat add() nebo equals()… Třeba máte dvě instance reprezentující v DOM následující dva XML elementy:
<f:element xmlns:f="http://example.com/namespace" />
<x:element xmlns:x="http://example.com/namespace" />
Má equals() vracet true nebo false?
Opravdu Javisti automaticky počítají s tím, že uživatelsky implementovaný kód může dělat cokoliv? Na takovéhle prasení vážně nejsem zvyklý a celkem mě překvapuje.
S tím počítají všichni dobří programátoři. Nejde o to, že by ten kód dělal něco nepředvídatelného (i když i to se stává). Ale když mám třeba kolekci, za kterou mám lokální nebo dokonce síťovou databázi, počítám s tím, že volání add() může trvat dlouho nebo může skončit třeba síťovou výjimkou. Když volám equals() na databázové entitě, ověřím si, zda se identita zjišťuje podle primárního klíče, podle hodnot nebo podle čeho. A tak dále. U operátoru ale čekám, že budou jednoduché, nebudou blokovat, nebudou vyhazovat výjimky, budou mít jednoznačný význam. V tom je operátor == v Javě opravdu trochu nešťastný, protože tam je potřeba vědět, že porovnává reference. Ale zrovna C++ to má stejně, nemýlím-li se.
-
JS pretezovani operatoru take neumoznuje.
Sice ne, ale zrovna pro JavaScriptové == existuje spousta takových těch příkladů–chytáků, kdy se máte snažit uhodnout, jak se ten kód vyhodnotí – a málokdo to trefí.
-
JS pretezovani operatoru take neumoznuje.
To nepopírám, ani netvrdím opak. Spíš jsem tím myslel to, že se JavaScriptu == a === vyčítá. :)
-
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.
protože operátory jsou vždy v nějakém případě kontraintuitivní
Můžete uvést příklad z Elixiru nebo Rustu?
moznych jinych reseni
lepších řešení?
Nebyl jste to vy, kdo mi vyčítal, že mluvím za vás a podsouvám vám něco, co jste netvrdil?
-
To, že operátor == v Javě porovnává hodnoty primitivních typů a reference u objektů mi nijak neintuitivní nepřipadá.
Je to kontraintuitivni - jestlize o Jave nic nevim, muzu si rict tak leda "WTF" - vubec me nenapadne, proc by se to jednou melo chovat tak a jindy jinak.
Uz to samotne vase "jenom pro větší čísla" ukazuje na neco, co vypada divne. "Vetsi cisla"? To je co? Vime to? Je to nekde v dokumentaci? Nebo se to chova "nahodne" (podle toho, jak velky je zrovna ten pool)?
Kdo o Javě nic neví, tak musí pochopit, že == na objektech porovnává reference. A že pokud porovnává objekty, které neudělal, tak je to implementačně závislá věc. Ten efekt se může dít u jakékoli knihovny apod.
Myslím, že u jakékoli jiné implementace toho operátoru bych musel napsat mnohem víc, abych vysvětlil jeho chování. A že takové jazyky máme a jsou i rozšířené :-)
Myslím, že vzhledem k tématu tohoto vlákna je to jedno, proč to Java má tak jak to má. Intuitivní to není, a to je to, oč tu běží. Já bych takovouto schízu v Ideálním jazyce nechtěl.
-
Tak tady bych silně nesouhlasil. Jsem C++ programátor a od operátorů očekávám, že čertoviny dělat nebudou. A taky ve všech knihovnách, které používám, žádné čertoviny nedělají.
Třeba to, že se operátor > používal pro zápis do streamu (což je blokující operace, navíc může vyhodit výjimku), tu už se v C++ nedělá?
Zrovna iostreamy jsou obecně jedna z nejhůř navržených (a taky nejstarších) částí standardní knihovny obecně. Na druhou stranu zrovna tady je z kontextu dost jasné, že tenhle operator<< opravdu nedělá nic jako bitový posun. Naproti tomu se potkávám se spoustou matematických knihoven, kde operátory dělají přesně to, co od nich intuitivně čekám.
Úplně stejně očekávám od metod, že budou dělat to, co vyplývá z jejich jména.
To já také. Jenže u spousty typů je tolik různých variant, co může dělat add() nebo equals()… Třeba máte dvě instance reprezentující v DOM následující dva XML elementy:
<f:element xmlns:f="http://example.com/namespace" />
<x:element xmlns:x="http://example.com/namespace" />
Má equals() vracet true nebo false?
No ale tohle je přesně to, na co narážím. Funkce equals nebo operátor== můžou být úplně stejně neintuitivní a matoucí. Zákaz přetěžování operátorů nevyřeší vůbec nic. Když autor knihovny pojmenuje funkci blbě, tak je vlastně úplně jedno jaké přesné ascii znaky to jsou.
Opravdu Javisti automaticky počítají s tím, že uživatelsky implementovaný kód může dělat cokoliv? Na takovéhle prasení vážně nejsem zvyklý a celkem mě překvapuje.
S tím počítají všichni dobří programátoři. Nejde o to, že by ten kód dělal něco nepředvídatelného (i když i to se stává). Ale když mám třeba kolekci, za kterou mám lokální nebo dokonce síťovou databázi, počítám s tím, že volání add() může trvat dlouho nebo může skončit třeba síťovou výjimkou. Když volám equals() na databázové entitě, ověřím si, zda se identita zjišťuje podle primárního klíče, podle hodnot nebo podle čeho. A tak dále. U operátoru ale čekám, že budou jednoduché, nebudou blokovat, nebudou vyhazovat výjimky, budou mít jednoznačný význam.
No když mám objekt, co se tváří třeba jako matice, tak od + i od add budu čekat sčítání matic. Ani v jednom případě nebudu očekávat komunikaci se vzdálenou databází. Naopak kolekce, která potichu komunikuje s nějakou vzdálenou databází je dost zákeřná, pokud se tváří jako nějaká obyčejná. Tohle zase nemá s operátory společné prakticky nic.
V tom je operátor == v Javě opravdu trochu nešťastný, protože tam je potřeba vědět, že porovnává reference. Ale zrovna C++ to má stejně, nemýlím-li se.
V C++ se operátor== obvykle přetěžuje tak, aby porovnával hodnoty. Obecně je v C++ zvykem, pokud to jenom trochu jde, aby se všechno chovalo jako vestavěné typy. Ale c++ rozlišuje samotný objekt od ukazatele na něj. Takže nemusím vůbec řešit, jak porovnávat identitu dvou objektů. Porovnávám jejich adresy resp. hodnoty dvou ukazatelů.
-
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.
To je ale jedno, ne? Prostě to je oficiálně nedefinované.
-
To je ale jedno, ne? Prostě to je oficiálně nedefinované.
Nevím jestli je to jedno a jestli zrovna tohle je vhodné nechat nedefinované.
Třeba ty zmíněné atomy v Erlangu jsou taky vnitřně uložené jako integery (takže obdoba ukazatelů), ale je jasně zadefinované, že když vytvářím atom třeba ze stringu, tak buďto v "cachi"/"poolu" už je, potom se ten integer zaručeně použije, nebo tam není, tak se vytvoří a pro příští volání se stejnou hodnotou bude platit první varianta.
Jasně, určitě existuje bambilion argumentů, proč to tak (alespoň v některých implementacích) javy není, nehodlám s nimi polemizovat. Čistě s BoneFlutem konstatuju, že mě by tohle v jazyce, ve kterém bych dělal, otravovalo.
-
Třeba ty zmíněné atomy v Erlangu jsou taky vnitřně uložené jako integery (takže obdoba ukazatelů), ale je jasně zadefinované, že když vytvářím atom třeba ze stringu, tak buďto v "cachi"/"poolu" už je, potom se ten integer zaručeně použije, nebo tam není, tak se vytvoří a pro příští volání se stejnou hodnotou bude platit první varianta.
Nebo jednou větou: podle mě by to singleton buď měl nebo neměl být z definice.
-
...
Jasně, určitě existuje bambilion argumentů, proč to tak (alespoň v některých implementacích) javy není, nehodlám s nimi polemizovat. Čistě s BoneFlutem konstatuju, že mě by tohle v jazyce, ve kterém bych dělal, otravovalo.
Nejak si nevzpominam, ze by me zrovna kes integeru nejak otravovala. Mas priklad kde by to mel byt problem, ktery vyzaduje pozornost?
-
Nejak si nevzpominam, ze by me zrovna kes integeru nejak otravovala. Mas priklad kde by to mel byt problem, ktery vyzaduje pozornost?
Jakykoliv priklad, kde jako zacatecnik budu predpokladat, ze 1000 je 1000 :)
-
No ale tohle je přesně to, na co narážím. Funkce equals nebo operátor== můžou být úplně stejně neintuitivní a matoucí. Zákaz přetěžování operátorů nevyřeší vůbec nic. Když autor knihovny pojmenuje funkci blbě, tak je vlastně úplně jedno jaké přesné ascii znaky to jsou.
V Javě právě úplně stejně neintuitivní a matoucí být nemohou. Protože operátor == je definován specifikací Javy a žádný uživatel nemůže udělat matoucí variantu operátoru ==. Jediné, co je na operátoru == v Javě matoucí, je to, že ho lze použít i na porovnání objektových typů a pak porovnává reference. Na druhou stranu, když se podíváte na okolní jazyky, má to tak i C, C++, JavaScript – a pro někoho by zase bylo matoucí, kdyby Java operátor == pro objekty měla zakázaný.
-
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.
protože operátory jsou vždy v nějakém případě kontraintuitivní
Můžete uvést příklad z Elixiru nebo Rustu?
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ží.
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.
-
No ale tohle je přesně to, na co narážím. Funkce equals nebo operátor== můžou být úplně stejně neintuitivní a matoucí. Zákaz přetěžování operátorů nevyřeší vůbec nic. Když autor knihovny pojmenuje funkci blbě, tak je vlastně úplně jedno jaké přesné ascii znaky to jsou.
V Javě právě úplně stejně neintuitivní a matoucí být nemohou. Protože operátor == je definován specifikací Javy a žádný uživatel nemůže udělat matoucí variantu operátoru ==. Jediné, co je na operátoru == v Javě matoucí, je to, že ho lze použít i na porovnání objektových typů a pak porovnává reference. Na druhou stranu, když se podíváte na okolní jazyky, má to tak i C, C++, JavaScript – a pro někoho by zase bylo matoucí, kdyby Java operátor == pro objekty měla zakázaný.
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í.
-
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.
Nemohu souhlasit. Stačí se na to dívat tak, že porovnáváte hodnotu versus identitu, a není problém.
Co naopak problém je, jsou technické/výkonnostní potíže. Ostatně proto, se soukromě domnívám, ty objekty jsou řešené jako reference.
-
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í.
O tom, že tím ideálním programovacím jazykem je Lisp, snad nikdo nepochybuje.
-
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í.
Už dlouho se tu řeší jedna odbočka. Bavíme se tu o Javě, C, C++, JavaScriptu, takže je snad každému jasné, že ideální programovací jazyk se tu opravdu neřeší.
Nemohu souhlasit. Stačí se na to dívat tak, že porovnáváte hodnotu versus identitu, a není problém.
Je to problém, protože u spousty struktur nedokážete určit ani co je to ta identita, o hodnotě ani nemluvě.
-
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í?
-
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)
-
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í.
Už dlouho se tu řeší jedna odbočka. Bavíme se tu o Javě, C, C++, JavaScriptu, takže je snad každému jasné, že ideální programovací jazyk se tu opravdu neřeší.
Proto ti to připomínám.
Nemohu souhlasit. Stačí se na to dívat tak, že porovnáváte hodnotu versus identitu, a není problém.
Je to problém, protože u spousty struktur nedokážete určit ani co je to ta identita, o hodnotě ani nemluvě.
Tak určitě ::)
-
Nebo jednou větou: podle mě by to singleton buď měl nebo neměl být z definice.
O Integeru a Stringu v Javě nikdy nikdo netvrdil, že je to singleton. Naopak třeba enumy jsou singletony. A že mají třídy Integer a String optimalizaci a někdy použijí už existující instanci, když je to možné, to ničemu nevadí, protože –jak praví klasik – na funkci to nemá vliv. Ale v žádném případě to z těch instancí nedělá singletony.
-
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... ;D
Nebo jednou větou: podle mě by to singleton buď měl nebo neměl být z definice.
O Integeru a Stringu v Javě nikdy nikdo netvrdil, že je to singleton. Naopak třeba enumy jsou singletony. A že mají třídy Integer a String optimalizaci a někdy použijí už existující instanci, když je to možné, to ničemu nevadí, protože –jak praví klasik – na funkci to nemá vliv. Ale v žádném případě to z těch instancí nedělá singletony.
No já Javu znám těžce okrajově, ale to "to ničemu nevadí" tak, z předcházející diskuze, nevypadá, když má vliv na návratové hodnoty operátoru.
-
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... ;D
Zmínil bych Haskell. Ten mi přijde těžkej, ale matoucí ne. Většinou jsem se dostal do situace, kdy jsem si říkal, že jsem prostě jen málo inteligentní. Ale, že by dělal něco nelogického, nebo matoucího, to ne.
Z opačné barikády bych zmínil jazyk Lua. Občas jsem tam narážel na to, že mi něco scházelo, nebo tak. Ale že bych měl potřebu mu něco vytýkat, to ne.
Z novějších jazyků nemůžu sloužit. Nemám je ještě tak pod kůží.
Pak je samozřejmě třeba definovat, co že to považujeme za matoucí, a jak moc. Zvyknout se dá na všechno :-)
-
Je to problém, protože u spousty struktur nedokážete určit ani co je to ta identita, o hodnotě ani nemluvě.
Hodnota není až takový problém.
Každý složený datový typ jde porovnat prvek po prvku. Invarianty na tom nic nezmění, ty jenom vyloučí některé možnosti. Navíc se takovéhle porovnání dá generovat i automaticky.
Co tím může zamíchat je zavedení nějakých tříd ekvivalence (zlomky, floaty). Nejjednodušší způsob, jak je zavést konzistentně je nějaká forma normalizace.
Pokud nějaký objekt nemá jít porovnat hodnotou, tak je to spíš proto, že to má být neprůhledný blackbox, než že by ani interně žádnou hodnotu neměl.
-
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... ;D
Zmínil bych Haskell. Ten mi přijde těžkej, ale matoucí ne. Většinou jsem se dostal do situace, kdy jsem si říkal, že jsem prostě jen málo inteligentní. Ale, že by dělal něco nelogického, nebo matoucího, to ne.
Z opačné barikády bych zmínil jazyk Lua. Občas jsem tam narážel na to, že mi něco scházelo, nebo tak. Ale že bych měl potřebu mu něco vytýkat, to ne.
Z novějších jazyků nemůžu sloužit. Nemám je ještě tak pod kůží.
Ano, tak to se asi shodneme. Byť třeba čísla mi v Haskellu zpočátku nepřišla dvakrát intuitivní (fromIntegral, apod.). Ale ano, na tom jazyku se dost projevuje ta vyšší vstupní bariéra. :)
Pak je samozřejmě třeba definovat, co že to považujeme za matoucí, a jak moc. Zvyknout se dá na všechno :-)
+1
-
...
No já Javu znám těžce okrajově, ale to "to ničemu nevadí" tak, z předcházející diskuze, nevypadá, když má vliv na návratové hodnoty operátoru.
Jakej vliv ma ta kes na navratove hodnoty operatoru?
-
No já Javu znám těžce okrajově, ale to "to ničemu nevadí" tak, z předcházející diskuze, nevypadá, když má vliv na návratové hodnoty operátoru.
Nemá vliv na návratové hodnoty operátoru. Operátor == v Javě při použití na objekty porovnává reference. Integer ani String nejsou definovány jako singletony, takže v běžící aplikaci může existovat víc instancí, které shodou okolností obsahují stejnou hodnotu. Pokud tedy operátorem == porovnávám reference ukazující na stejnou hodnotu, je věcí náhody, zda dostanu true nebo false. Pool opakovaně používaných instancí akorát zvyšuje pravděpodobnost, že se pro stejnou hodnotu použijí stejné instance, ale nikdo nemůže zaručit, že nevznikne jiná instance se stejnou hodnotou.
Každopádně pokud někdo chce v Javě porovnávat hodnoty objektů a použije ==, je to těžce začátečnická chyba. Připadá mi zbytečné pořád dokola řešit, co se stane, když někdo takovouhle hrubou chybu udělá. Je to jako bychom řešili, že někdo chtěl dvě čísla sečíst a místo toho je odečetl, a pak bychom dlouze zkoumali, jak moc je matoucí, že pokud byl druhý operátor 0, dostal přesto správný výsledek.
Ano, v Javě a v mnoha dalších jazycích když se použije == pro porovnání referencí, ale programátor si bude myslet, že porovnává hodnoty, může někdy dostat „správný“ výsledek (tedy ten, který by dostal při porovnání hodnot). Každý, kdo chce programovat v jazyku, kde == porovnává reference, ale tohle musí vědět. Na rozdíl mezi == a equals() se ptáme u pohovorů na juniorní programátory těch uchazečů, u kterých vidíme, že s programováním začínají. Pokročilejšího juniora bych se styděl na takovou samozřejmost zeptat.
-
V javě sice neprogramuju, ale z mého pohledu není problém samotné chování "==" ale spíš kombinace s umožněním zápisu:
Integer a = 1024;
Vlevo objekt, vpravo primitivní typ, já bych už křičel při syntaktické kontrole, že správně má být něco jako:
Integer a;
a.value = 1024;
nebo
Integer a = {value:1024};
Pak to upozorní každého, že Integer není int.
Nebo kompilátoru vysvětlit, že pro konkrétní vestavěné objektové typy se == prostě automaticky používá na hodnoty a ne na reference. Protože nevím o žádném smysluplném důvodu porovnávat reference na dva objekty typu Integer.
-
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.
protože operátory jsou vždy v nějakém případě kontraintuitivní
Můžete uvést příklad z Elixiru nebo Rustu?
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ží.
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.
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.
-
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... ;D
Zmínil bych Haskell. Ten mi přijde těžkej, ale matoucí ne.
Spíš jen jinej.
-
V javě sice neprogramuju, ale z mého pohledu není problém samotné chování "==" ale spíš kombinace s umožněním zápisu:
Integer a = 1024;
Vlevo objekt, vpravo primitivní typ, já bych už křičel při syntaktické kontrole, že správně má být něco jako:
Integer a;
a.value = 1024;
nebo
Integer a = {value:1024};
Pak to upozorní každého, že Integer není int.
Nebo kompilátoru vysvětlit, že pro konkrétní vestavěné objektové typy se == prostě automaticky používá na hodnoty a ne na reference. Protože nevím o žádném smysluplném důvodu porovnávat reference na dva objekty typu Integer.
To tě nezachrání:
Integer a = {value:1024};
// dlouhej kód
if (a == b) {
}
A v Javě je dlouhej kód úplně cokoliv :)
-
Hodnota není až takový problém.
Každý složený datový typ jde porovnat prvek po prvku. Invarianty na tom nic nezmění, ty jenom vyloučí některé možnosti. Navíc se takovéhle porovnání dá generovat i automaticky.
Co tím může zamíchat je zavedení nějakých tříd ekvivalence (zlomky, floaty). Nejjednodušší způsob, jak je zavést konzistentně je nějaká forma normalizace.
Problém je, že těch tříd ekvivalence často může být několik. Prostě někdy chcete, aby 1/2 byla to samé jako 2/4, a někdy to nechcete.
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.
Vážně? A jak daleko s tím chcete zajít? 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…
-
Nebo kompilátoru vysvětlit, že pro konkrétní vestavěné objektové typy se == prostě automaticky používá na hodnoty a ne na reference. Protože nevím o žádném smysluplném důvodu porovnávat reference na dva objekty typu Integer.
Tohle už je flíkování, které jenom odsouvá problém dál. Ten problém vychází z toho, že jeden operátor dělá různé věci pro různé typy (jednou porovnává umístění, jindy co na tom místě leží). Systematické řešení by bylo vybrat jen jednu možnost.
V ideálním jazyce by nemělo jít poznat, jestli je nějaký typ vestavěný nebo implementovaný v knihovně.
-
To tě nezachrání:
Integer a = {value:1024};
// dlouhej kód
if (a == b) {
}
A v Javě je dlouhej kód úplně cokoliv :)
Nejde o záchranu, jde o to, že zápis Integer a = 1024 mě utvrzuje v tom, že s tím můžu pracovat jako s hodnotou, protože jsem tam vložil primitivní hodnotu.
Když v c# použiju typy Int16, Int32, Int64 tak jsou to myslím taky objekty (mají na sobě nějaké metody), ale při použití snad jakéhokoliv operátoru se chovají jko primitivní typy.
-
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.
protože operátory jsou vždy v nějakém případě kontraintuitivní
Můžete uvést příklad z Elixiru nebo Rustu?
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ží.
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.
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.
Ztracim se. Prece se tu rikalo, ze problem neni v tom operatoru, ale v te kesi....
-
V javě sice neprogramuju, ale z mého pohledu není problém samotné chování "==" ale spíš kombinace s umožněním zápisu:
Integer a = 1024;
Vlevo objekt, vpravo primitivní typ, já bych už křičel při syntaktické kontrole, že správně má být něco jako:
Integer a;
a.value = 1024;
nebo
Integer a = {value:1024};
Pak to upozorní každého, že Integer není int.
Dříve to tak v Javě bylo. Když se zjistilo, že primitivní typy byly omyl, zavedl se autoboxing/unboxing, kdy se mezi primitivním typem a jeho objektovou variantou převádí automaticky. Tohle je jeden důsledek. Kde kdo tvrdí, jak je Java ukecaná, a když se nějaký zápis vynechá, je to taky špatně… Ale v praxi tohle žádný problém není, protože programátoři za prvé vědí, že == porovnává reference, za druhé se objektové Integery používají nejčastěji pro identifikátory (např. databázové – protože umožňují mít null pro nové entity bez identifikátoru), které se v aplikaci málokdy porovnávají. A za třetí při porovnání referencí vás bude varovat IDE, protože je to opravdu jen výjimečná operace a takové použití spíš vypadá na chybu.
Nebo kompilátoru vysvětlit, že pro konkrétní vestavěné objektové typy se == prostě automaticky používá na hodnoty a ne na reference. Protože nevím o žádném smysluplném důvodu porovnávat reference na dva objekty typu Integer.
To by teprve byl zmatek, kdyby se objekty někdy porovnávaly podle reference a někdy podle hodnoty.
-
Ztracim se. Prece se tu rikalo, ze problem neni v tom operatoru, ale v te kesi....
Ne,v keši žádný problém není. Problém je jenom v programátorovi, který použil špatný operátor, když měl použít metodu equals().
-
Hodnota není až takový problém.
Každý složený datový typ jde porovnat prvek po prvku. Invarianty na tom nic nezmění, ty jenom vyloučí některé možnosti. Navíc se takovéhle porovnání dá generovat i automaticky.
Co tím může zamíchat je zavedení nějakých tříd ekvivalence (zlomky, floaty). Nejjednodušší způsob, jak je zavést konzistentně je nějaká forma normalizace.
Problém je, že těch tříd ekvivalence často může být několik. Prostě někdy chcete, aby 1/2 byla to samé jako 2/4, a někdy to nechcete.
Pokud implementuju zlomky, pak je to stejné. Pokud implementuju dvojice čísel, pak je to různé. Rozliším to typovým systémem. Pokud se na to koukám jako na hodnoty, tak dává smysl i občasná konverze mezi tím zlomkem a dvojicí čísel.
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…
Jsou stejné. Liší se počet bytů, ne znaků. Našemu pojmu znak (ve významu používaném v běžném životě) spíš odpovídá pojem grapheme cluster, které mají oba řetězce po jednom. Zase je to o tom, jestli chci pracovat s textem, nebo polem bytů a tohle už můžu odlišit na úrovni typového systému.
-
A za třetí při porovnání referencí vás bude varovat IDE, protože je to opravdu jen výjimečná operace a takové použití spíš vypadá na chybu.
Tohle beru jako rozumné řešení problému - upozornění, že by to mohla být blbost, to rozumného programátora (i začínajícího) trkne.
To by teprve byl zmatek, kdyby se objekty někdy porovnávaly podle reference a někdy podle hodnoty.
Je to jen aplikace unboxingu konkrétních typů na další operátor. Když se to může dít při jiných operacích (přiřazování, vkládání parametrů), je jen věcí dohody, že se to může dít i při porovnávání.
-
To by teprve byl zmatek, kdyby se objekty někdy porovnávaly podle reference a někdy podle hodnoty.
Neřešíme už tu několik stránek to, že přesně tohle se v Javě děje? Vestavěné typy se porovnávají podle hodnoty a naše podle reference. ::)
-
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.
-
Pokud implementuju zlomky, pak je to stejné.
A nebo různé, záleží na okolnostech. Třeba když budu implementovat systém pro matematickou sazbu (TeX), rozhodně nechci, aby mi klidně místo 2/4 vysázel 1/2, protože je to přece stejné.
Jsou stejné. Liší se počet bytů, ne znaků. Našemu pojmu znak (ve významu používaném v běžném životě) spíš odpovídá pojem grapheme cluster, které mají oba řetězce po jednom. Zase je to o tom, jestli chci pracovat s textem, nebo polem bytů a tohle už můžu odlišit na úrovni typového systému.
Neliší se jen počet bytů, liší se i počet znaků. V první textu je jeden znak „č“, v druhém textu jsou dva znaky – malé „c“ a za ním háček pro kombinování. Aby byly ty řetězce shodné i na počet znaků, musíte nejprve provést jejich normalizaci – pak budou opravdu na bit stejné. Ale opět – někdy nechcete provádět žádnou normalizaci, někdy chcete provádět standardní normalizaci Unicode, a někdy chcete řetězce porovnávat třeba bez ohledu na velikost písmen a na diakritiku.
Je to jen aplikace unboxingu konkrétních typů na další operátor. Když se to může dít při jiných operacích (přiřazování, vkládání parametrů), je jen věcí dohody, že se to může dít i při porovnávání.
Unboxing se neaplikuje na operátory, ale na výskyt konkrétních hodnot (konstant nebo proměnných). A unboxing se v Javě nikdy nedělá svévolně, že by kód byl validní i bez unboxingu, ale kompilátor se rozhodl, že typy změní. Autoboxing/unboxing se aplikuje jedině v případě, že daný kód nejde přeložit, ale aplikováním boxingu/unboxingu přeložit půjde. Tedy se aplikuje jenom tam, kde je ve zdrojovém kódu nesoulad typů, např. máte metodu, která má jako parametr velký Integer, ale cpete do něj malý int. Na kódu Integer == int se uplatní, protože jinak by to nešlo přeložit. Integer == Integer je ale validní javovský kód, a kompilátoru nepřísluší hodnotit, jak moc je takový kód pravděpodobný.
-
Tak se jeste zeptam, abych vedel co je idealni ;)
Clojure nema operatory, ale:
Mame funkci (=) , ktera porovnava hodnoty.
Funkci (identical?) , ktera porovnava reference.
A pak jeste specialne pro cisla funkci (==), ktera porovnava hodnoty nezavisle na typu.
Je to "idealni"?
-
Neřešíme už tu několik stránek to, že přesně tohle se v Javě děje? Vestavěné typy se porovnávají podle hodnoty a naše podle reference. ::)
Ne, neděje. Už jsem to psal několikrát, primitivní typy se porovnávají podle hodnoty, objekty se porovnávají podle reference. String je taky vestavěný typ, ale je to objekt, takže se porovnává podle reference. Java obecně se hodně snaží vyhýbat magii s vestavěnými typy a pojem „vestavěný typ“ se u Javy ani nepoužívá. Vlastně jediná „magie“, která mne napadá, je sčítání řetězců. Jinak se v Javě snad všechno tváří jako uživatelské typy – jako třídy, které by mohly být kompletně implementovány koncovým programátor.
-
U zlomků by jeden očekával algebraickou rovnost
Myslí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.
u floatu se taky rovná 1.2 a 1.2000000, pokud na ně tedy neudělám napřed toString(), hejže?
Po té, co se začínající programátoři v druhé lekci dozví, jaký je rozdíl mezi hodnotou a referencí, a že objekty nikdy nemají porovnávat pomocí ==, hned ve třetí lekci se dozví, že floaty se nikdy neporovnávají na rovnost. float 1.2 se nerovná floatu 1.2000000. A to není žádná specialita Javy, tak to mají všechny jazyky, které používají čísla s plovoucí čárkou dle IEEE-754.
Ostatně moderní jazyky to takto mají právě z toho důvodu, že to dává největší smysl.
Problém je v tom, že někdy prostě nejde vybrat, co dává největší smysl. Uváděl jsem tu příklad s elementem z DOM XML – tam dávají stejný smysl všechny tři varianty. Porovnávat identitu, porovnávat název elementu a jmenný prostor, a porovnávat název elementu, jmenný prostor i prefix jmenného prostoru.
-
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í ==.
Omyl. 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.
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é.
protože operátory jsou vždy v nějakém případě kontraintuitivní
Můžete uvést příklad z Elixiru nebo Rustu?
Tak třeba Rust má spoustu operátorů, které nejsou vůbec intuitivní, to je problém sám o sobě.
Ok. A ten příkad teda?
protože u složitějších typů vždy narazíme na to, že prostě není jasné, co je to rovnost.
Nevím, proč bysme měli. Třídy ekvivalence mají být součástí definice typu. Jistě, jsou situace, kdy se dají nadefinovat různě. To už je na programátorovi, jak si je chce nadefinovat. Ale nevidím v tom sebemenší problém, natož abychom na něj "vždy naráželi".
Podívejte se třeba, jak má Haskell nadefinovanou třídu Eq. Problém je kde?
-
Ztracim se. Prece se tu rikalo, ze problem neni v tom operatoru, ale v te kesi....
Filip má velmi efektivní selektivní vnímání ;)
-
Omyl. 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.
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é.
Když ono to z vašich komentářů opravdu vypadá, že netušíte, o čem píšete.
Napsal jste, že programátor chtěl porovnávat reference. A pomocí == opravdu reference porovnává. Byla to neznalost programátora? Ne. Byla to chyba programátora? Ne. Fungoval mu program správně v testu s malými čísly? Ano. Fungoval mu správně na produkci s velkými čísly? Ano. Vracelo porovnání true, když byly reference stejné? Ano. Vracelo porovnání false, pokud byly reference jiné? Ano. Snažil se Mirek Prýmek popsat nějaký problematický příklad, a přitom popsal příklad, ve kterém je vše v pořádku, vše funguje správně a podle programátorova očekávání? Ano.
Jediná chyba tedy byla v tom, že jste si myslel, že je tam něco špatně. Takže mi z toho vychází akorát: Rozumí Mirek Prýmek rozdílu mezi hodnotou a referencí v Javě, rozumí tomu, jak funguje pool Integerů? Ne.
Další váš omyl je v tom, že píšete o jazyce. Pool Integerů není žádná vlastnost jazyka, je to normálně implementované ve třídě Integer (a podobně ve třídě String). Nic vám nebrání, abyste si takovou třídu napsal sám, občas se to používá.
Podívejte se do implementace metody Integer.valueOf(int), jak je to naprogramované. Celý ten zázrak má tři řádky:
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
Ok. A ten příkad teda?
Ten následoval hned po té části, kterou jste citoval.
Nevím, proč bysme měli. Třídy ekvivalence mají být součástí definice typu. Jistě, jsou situace, kdy se dají nadefinovat různě. To už je na programátorovi, jak si je chce nadefinovat. Ale nevidím v tom sebemenší problém, natož abychom na něj "vždy naráželo".
Ano, takže jste sám potvrdil, že intuitivnost ekvivalence naráží na to, že pro stejné typy dávají smysl různé třídy ekvivalence. Můžete si sice nadefinovat, že Zlomek1 porovnává zlomek po převedení do základního tvaru a Zlomek2 porovnává zlomky tak, jak jsou, ale intuitivní to není. A pak si ještě zkusíte porovnat Zlomek1 se Zlomkem2, a vzápětí Zlomek2 se Zlomkem1, a máte z toho pořádný guláš.
-
Filip má velmi efektivní selektivní vnímání ;)
Nikoli, já pouze vím – na rozdíl od vás – jak to funguje.
-
Ještě abyste pochopil ten rozdíl:
assert !(new Integer(1) == new Integer(1));
boolean b = Integer.valueOf(1) == Integer.valueOf(1);
assert b || !b;
Na prvním řádku se vytvářejí dvě instance, musí to tedy být dvě různé reference. Operátor == u objektů porovnává reference, takže rovnost na prvním řádku nikdy nemůže být splněna.
Na druhém řádku se volá statická metoda, ta mi vrátí nějakou referenci. Nikde není řečeno, zda pro stejné hodnoty to bude vracet jeden objekt nebo dva různé objekty. V té metodě klidně může být orákulum, které vám v polovině případů bude vracet objekt z poolu a v druhé polovině případů nové instance. Takže o té rovnosti nelze bez znalosti konkrétní implementace rozhodnout, zda platí nebo neplatí.
-
U zlomků by jeden očekával algebraickou rovnost
Myslí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.
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 nevyhodnocuje ::)
-
Omyl. 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.
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é.
Když ono to z vašich komentářů opravdu vypadá, že netušíte, o čem píšete.
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ěl ;)
-
Filip má velmi efektivní selektivní vnímání ;)
Nikoli, já pouze vím – na rozdíl od vás – jak to funguje.
Určitě? :o
-
U zlomků by jeden očekával algebraickou rovnost
Myslí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.
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 nevyhodnocuje ::)
Ono je tu teď od tohoto autora těžké najít ne nesmírně hloupý příklad.
Filip má velmi efektivní selektivní vnímání ;)
Nikoli, já pouze vím – na rozdíl od vás – jak to funguje.
Ne, to už víme všichni. Problém je, že je to random, což se tu tak nějak snažíme vysvětlit. ;)
-
Fungoval mu program správně v testu s malými čísly? Ano. Fungoval mu správně na produkci s velkými čísly? Ano.
Ne, nefungovalo mu to na produkci s velkými čísly. Protože se mylně domníval, že to v Javě funguje jako v Erlangu.
Blik?
-
Na druhém řádku se volá statická metoda, ta mi vrátí nějakou referenci. Nikde není řečeno, zda pro stejné hodnoty to bude vracet jeden objekt nebo dva různé objekty. V té metodě klidně může být orákulum, které vám v polovině případů bude vracet objekt z poolu a v druhé polovině případů nové instance. Takže o té rovnosti nelze bez znalosti konkrétní implementace rozhodnout, zda platí nebo neplatí.
Ano, já to naprosto chápu. A jednoduše si nepřeju, aby se jazyk, se kterým pracuju, choval nepředvídatelným způsobem, podle orákula, podle denní doby, podle nálady v RedMondu nebo aktuální výše zlatých padáků v Redwood Shores. Obzvláště ne pokud se to týká základních, stupidně jednoduchých typů jako "integer o něco větší než padesát".
-
Každé jedno špatné rozhodnutí, na kterém se trvá, vede k sérii marných snah o nápravu a pak je jako "řešení" nabídnuto vysvětlení, proč to je vlastně v pořádku.
Každý programátor, pokud mu někdo v rané fázi vývoje nevymyje hlavičku, očekává a chce, aby se čísla sčítala pomocí + a odčítala pomocí -. Jelikož se někdo, kdo rozhodoval o tom, co v Javě smí být a co nesmí, bál "přetěžování operátorů", má Java čísla, která se sčítají normálně přes + a pak čísla, která používají "add()", protože to "jinak nejde".
Úplně stejné to je s tím == a .equals(). Samozřejmě, že "každý" chce porovnávat dvě hodnoty a ne "identitu", když sáhne po ==. Jenže Řád zlatého šálku si na to vymyslí equals() a když děcko řekne, že císař je nahý, v poklidu děcko obviní z hlouposti nebo neznalosti a odkáže ho k četbě posvátných svitků, kde je jasně uvedeno, že císař je oděn vznešeností, což, jak jistě uznáte, není totéž co nahota.
-
Každé jedno špatné rozhodnutí, na kterém se trvá, vede k sérii marných snah o nápravu a pak je jako "řešení" nabídnuto vysvětlení, proč to je vlastně v pořádku.
Každý programátor, pokud mu někdo v rané fázi vývoje nevymyje hlavičku, očekává a chce, aby se čísla sčítala pomocí + a odčítala pomocí -. Jelikož se někdo, kdo rozhodoval o tom, co v Javě smí být a co nesmí, bál "přetěžování operátorů", má Java čísla, která se sčítají normálně přes + a pak čísla, která používají "add()", protože to "jinak nejde".
Úplně stejné to je s tím == a .equals(). Samozřejmě, že "každý" chce porovnávat dvě hodnoty a ne "identitu", když sáhne po ==. Jenže Řád zlatého šálku si na to vymyslí equals() a když děcko řekne, že císař je nahý, v poklidu děcko obviní z hlouposti nebo neznalosti a odkáže ho k četbě posvátných svitků, kde je jasně uvedeno, že císař je oděn vznešeností, což, jak jistě uznáte, není totéž co nahota.
Vidím, že vám došly argumenty, tak jste přešel na demagogii. Jaké problémy má vaše řešení s + a == jsem napsal. Mohl jste na ty argumenty věcně reagovat. Vy jste zvolil jinou variantu, tváříte se, že jste žádné argumenty neslyšel.
Pokud jste to nepochopil, argumenty vám ještě zopakuju. Problém vašeho teoretického řešení je v tom, že neexistuje jednoznačná hranice mezi tím, co je a není číslo, stejně jako často neexistuje jednoznačná definice toho, co znamená porovnání hodnot.
Přípustné a legitimní jsou samozřejmě oba dva způsoby. Jak ten, který používá třeba C++ – zavedeme do jazyka novou kategorii „číslo“, přesněji „cokoli, co se dá sčítat“ (aniž by ta kategorie nutně musela být ve specifikaci), a umožníme každému programátorovi, aby si do této kategorie zařadil své typy. Výhodou je, že se pak opravdu dá zajistit, že všechna „čísla“ se sčítají pomocí +. Nevýhoda je ta, že se lidé neshodnou na tom, co vše patří pod čísla, takže se pak programátor musí učit další věc, která navíc platí jenom v daném projektu – co jsou a co nejsou čísla. To, že si v C++ vlastně můžu vytvořit svůj vlastní jazyk, který velice stručně popisuje mou třídu problému, je velice silná vlastnost C++ – proto je také tak oblíbené. Zároveň ta vlastně nepřeberná šíře možností, která způsobuje, že už snad nikdo nemůže znát celý jazyk, je silným negativem, pro které se spousta lidí C++ vyhýbá.
Java zvolila opačný přístup. Jazyk zná jenom dva druhy datových typů – primitivní typy a objekty. Nic jiného, Java programátorovi se nemůže stát, že by v program najednou narazil na nový druh typů, třeba na „čísla“. Znamená to, že Java má velmi malé množství pravidel, toho, co programátor musí znát. Všechno dalšího je postavené na téhle základní sadě pravidel. Tahle jednoduchost jazyka je velice silná vlastnost Javy – proto je také tak oblíbená. Zároveň to způsobuje, že příslušnou třídu problému musíte popsat jen pomocí objektového paradigmatu, tříd a metod – nic jiného vám Java nenabízí. To způsobuje onu pověstnou ukecanost Javy a to, že na kde co musíte mít vybudovaný objektový framework, který teprve dá programátorovi do ruky ty nástroje, které bude používat. Což je silné negativum, pro které se spousta lidí Javě vyhýbá.
Mimochodem, je pozoruhodné, že se to chování, že + sčítá jen primitivní typy a == porovnává primitivní typy podle hodnot a objekty podle referencí, se tu vytýká zrovna Javě, která má tuhle základní sadu pravidel nejmenší z podobných často používaných jazyků. Přitom stejná pravidla platí v C, C++, JavaScriptu nebo Pythonu. V C++ nebo Pythonu ty operátory můžete (ale nemusíte) přetížit. Ale klidně si dál myslete, že C, C++, Javu, JavaScript i Python navrhovali hlupáci, kteří chování operátorů navrhli špatně, a měli se s vámi nejdřív poradit.
-
Každé jedno špatné rozhodnutí, na kterém se trvá, vede k sérii marných snah o nápravu a pak je jako "řešení" nabídnuto vysvětlení, proč to je vlastně v pořádku.
Každý programátor, pokud mu někdo v rané fázi vývoje nevymyje hlavičku, očekává a chce, aby se čísla sčítala pomocí + a odčítala pomocí -. Jelikož se někdo, kdo rozhodoval o tom, co v Javě smí být a co nesmí, bál "přetěžování operátorů", má Java čísla, která se sčítají normálně přes + a pak čísla, která používají "add()", protože to "jinak nejde".
Úplně stejné to je s tím == a .equals(). Samozřejmě, že "každý" chce porovnávat dvě hodnoty a ne "identitu", když sáhne po ==. Jenže Řád zlatého šálku si na to vymyslí equals() a když děcko řekne, že císař je nahý, v poklidu děcko obviní z hlouposti nebo neznalosti a odkáže ho k četbě posvátných svitků, kde je jasně uvedeno, že císař je oděn vznešeností, což, jak jistě uznáte, není totéž co nahota.
Jistě, ale vzhledem k tomu, kdy Java vznikla, je její návrh ještě poměrně snesitelný.
-
Tahle jednoduchost jazyka je velice silná vlastnost Javy – proto je také tak oblíbená.
Ve skutečnosti je i programování v shellu oblíbenější :)
https://insights.stackoverflow.com/survey/2019#technology-_-most-loved-dreaded-and-wanted-languages
-
Pokud jste to nepochopil, argumenty vám ještě zopakuju. Problém vašeho teoretického řešení je v tom, že neexistuje jednoznačná hranice mezi tím, co je a není číslo, stejně jako často neexistuje jednoznačná definice toho, co znamená porovnání hodnot.
Obecně sdílená představa o tom, co je číslo, je prokazatelně větší, než to, co Java milostivě umožňuje sčítat pomocí operátoru +. A nemusíme zacházet ani do nějakých specialit, stačí BigInteger, to je číslo z definice.
Přípustné a legitimní jsou samozřejmě oba dva způsoby. Jak ten, který používá třeba C++ – zavedeme do jazyka novou kategorii „číslo“, přesněji „cokoli, co se dá sčítat“ (aniž by ta kategorie nutně musela být ve specifikaci), a umožníme každému programátorovi, aby si do této kategorie zařadil své typy. Výhodou je, že se pak opravdu dá zajistit, že všechna „čísla“ se sčítají pomocí +. Nevýhoda je ta, že se lidé neshodnou na tom, co vše patří pod čísla, takže se pak programátor musí učit další věc, která navíc platí jenom v daném projektu – co jsou a co nejsou čísla. To, že si v C++ vlastně můžu vytvořit svůj vlastní jazyk, který velice stručně popisuje mou třídu problému, je velice silná vlastnost C++ – proto je také tak oblíbené. Zároveň ta vlastně nepřeberná šíře možností, která způsobuje, že už snad nikdo nemůže znát celý jazyk, je silným negativem, pro které se spousta lidí C++ vyhýbá.
Problém Tvého argumentu je, že ty způsoby nejsou dva, ale minimálně tři. Lisp, Haskell a další jazyky už desítky let ukazují, že "operátor" není nic jiného, než (typicky infixová) funkce (jejíž název není alfanumerický/slovní, nýbrž složený ze symbolů) se dvěma argumenty (no, ještě jsou unární a ternární, ale to už je detail). Že je vhodné nechat "operátoru" + význam sčítání, na tom se všichni asi shodnou. V tomto kontextu je zajímavé, že operuješ zrovna C++ a ne alespoň C#, což je jazyk bližší Javě a "přetěžování operátorů" má taky.
Mimochodem, je pozoruhodné, že se to chování, že + sčítá jen primitivní typy a == porovnává primitivní typy podle hodnot a objekty podle referencí, se tu vytýká zrovna Javě, která má tuhle základní sadu pravidel nejmenší z podobných často používaných jazyků. Přitom stejná pravidla platí v C, C++, JavaScriptu nebo Pythonu. V C++ nebo Pythonu ty operátory můžete (ale nemusíte) přetížit. Ale klidně si dál myslete, že C, C++, Javu, JavaScript i Python navrhovali hlupáci, kteří chování operátorů navrhli špatně, a měli se s vámi nejdřív poradit.
Tohle je straw man fallacy. Nikde jsem netvrdil, že nějaký jazyk navrhovali hlupáci. Ale třeba na C++ i Pythonu je vidět, že některá rozhodnutí tvůrci časem přehodnotili, Python se ve verzi 3 vydal dokonce cestou rozbití kompatibility (unicode vs string, zahození "klasických tříd", je toho hodně). Jak jsem psal v předchozím příspěvku - trvat na správnosti nějakého rozhodnutí poté, co se ukáže, že to byla chyba, je prostě kontraproduktivní. Mezitím na javovské platformě vznikly minimálně 4 další jazyky, které se snaží být lepší Javou a to přesto, že se (neochotně a pomalu) Java také vyvíjela.
Navíc by mě zajímalo, v čem podle Tebe dělá Python == jako Java. Dělá to stejně jenom u "hloupých" objektů, chytrý objekt si umí říct, jestli jeho vnitřek odpovídá druhému objektu.
-
Každé jedno špatné rozhodnutí, na kterém se trvá, vede k sérii marných snah o nápravu a pak je jako "řešení" nabídnuto vysvětlení, proč to je vlastně v pořádku.
Jistě, ale vzhledem k tomu, kdy Java vznikla, je její návrh ještě poměrně snesitelný.
Tohle je subjektivní, určitě to mohlo být horší a určitě i výrazně lepší (například dodnes kroutím hlavou nad hloupým původním návrhem kolekcí před zavedením generik). Spíš mi vadí ale ta zabedněnost, s jakou se u ní na některých zjevných chybách lpí.
-
Každé jedno špatné rozhodnutí, na kterém se trvá, vede k sérii marných snah o nápravu a pak je jako "řešení" nabídnuto vysvětlení, proč to je vlastně v pořádku.
Jistě, ale vzhledem k tomu, kdy Java vznikla, je její návrh ještě poměrně snesitelný.
Spíš mi vadí ale ta zabedněnost, s jakou se u ní na některých zjevných chybách lpí.
Někteří to tak mají, něco se horkotěžko naučí a pak na tom lpí.
-
Problém Tvého argumentu je, že ty způsoby nejsou dva, ale minimálně tři.
Asi jsem to měl napsat jasněji – těch způsobů je celá škála, C++ a Java jsou dva příklady, které jsou na opačných místech od středu, ale jsou těsně před extrémem. Jsou samozřejmě mnohé další jazyky, které používají něco mezi, a pak další méně používané jazyky, které používají nějakou extrémní variantu.
Lisp, Haskell a další jazyky už desítky let ukazují, že "operátor" není nic jiného, než (typicky infixová) funkce (jejíž název není alfanumerický/slovní, nýbrž složený ze symbolů) se dvěma argumenty (no, ještě jsou unární a ternární, ale to už je detail).
Ano, to je také možnost. Java se tomuhle záměrně vyhnula a udělala operátory jednoduché.
Že je vhodné nechat "operátoru" + význam sčítání, na tom se všichni asi shodnou.
Ano, na tom se všichni shodnou. Ale už asi počtvrté opakuju, že se všichni neshodnou na tom, co je sčítání.
V tomto kontextu je zajímavé, že operuješ zrovna C++ a ne alespoň C#, což je jazyk bližší Javě a "přetěžování operátorů" má taky.
Java přetěžování operátorů nemá, je to záměr, protože přetěžování operátorů komplikuje jazyk a činí kód méně předvídatelný. Vysvětloval jsem to v komentáři, na který reagujete, že programovací jazyk se pohybuje někde na škále od „pár jednoduchých nástrojů, ze kterých si sám poskládáte, co potřebujete“ po „spousta komplexních nástrojů“. Java je na té škále poměrně daleko u prvního bodu. Že vám vyhovuje to druhé je vaše věc, ale neznamená to, že je to jediné správné.
Tohle je straw man fallacy. Nikde jsem netvrdil, že nějaký jazyk navrhovali hlupáci. Ale třeba na C++ i Pythonu je vidět, že některá rozhodnutí tvůrci časem přehodnotili, Python se ve verzi 3 vydal dokonce cestou rozbití kompatibility (unicode vs string, zahození "klasických tříd", je toho hodně). Jak jsem psal v předchozím příspěvku - trvat na správnosti nějakého rozhodnutí poté, co se ukáže, že to byla chyba, je prostě kontraproduktivní. Mezitím na javovské platformě vznikly minimálně 4 další jazyky, které se snaží být lepší Javou a to přesto, že se (neochotně a pomalu) Java také vyvíjela.
Jenže to rozhodnutí se neukázalo jako špatné. To, že vám něco nevyhovuje, neznamená, že to bylo správně. Zvolené řešení (s operátorem ==) je při zachování ostatních vlastností jazyka jedno z nejlepších možných, neexistuje žádné lepší. Chybné rozhodnutí v Javě jsou primitivní typy, což je zřejmé dnes, ale v době vzniku Javy to bylo logické rozhodnutí, a kdyby to tak nebylo, dnes by nebyla žádná Java a nemohli bychom řešit, že dnes jsou primitivní typy špatně. Jedna z vlastností Javy je to, že se velmi úzkostlivě drží zpětná kompatibilita. Opět, je to záměr, je to jeden z podstatných důvodů, proč se Java používá tam kde se používá. Pokud nechcete držet zpětnou kompatibilitu a chcete rychlý vývoj jazyka, nepoužívejte Javu.
Python 3 je zrovna příklad toho, jak problematické je to rozbití zpětné kompatibility – teprve teď se reálně daří zbavovat Pythonu 2. Perl na tohle rozbití zpětné kompatibility dokonce prakticky umřel, resp. odešel do zapomnění – dříve byla dvojice Perl a Python, která spolu soupeřila, Python spíš Perl doháněl. Dneska o Perlu neslyšíte.
Navíc by mě zajímalo, v čem podle Tebe dělá Python == jako Java. Dělá to stejně jenom u "hloupých" objektů, chytrý objekt si umí říct, jestli jeho vnitřek odpovídá druhému objektu.
Tak si znovu přečtěte, co jsem napsal. Python ve výchozím stavu porovnává identitu objektů, ale je možné operátor přetížit.
Spíš mi vadí ale ta zabedněnost, s jakou se u ní na některých zjevných chybách lpí.
Problém je v tom, že to, co vy považujete za zjevné chyby, jsou ve skutečnosti přednosti. Na přednostech se opravdu lpí.
Někteří to tak mají, něco se horkotěžko naučí a pak na tom lpí.
V této diskusi ovšem máme jiný problém – někomu něco vyhovuje, a myslí si, že to tedy musí být jediné správně řešení a vše ostatní je špatně. Všimněte si na rozdíl v našich argumentech. Vy argumentujete „je to špatně, protože já mám radši něco jiného“. Já argumentuju „tenhle přístup má tyhle výhody a tyhle nevýhody, výhodné je to pro tyhle případy; opačný přístup má tyhle výhody a tyhle nevýhody, výhodné je to pro tyhle případy“. Podle mne ten, kdo zabedněně lpí jenom na svém přístupu a považuje ho za jediný správný, jste tu vy.
-
V této diskusi ovšem máme jiný problém – někomu něco vyhovuje, a myslí si, že to tedy musí být jediné správně řešení a vše ostatní je špatně. Všimněte si na rozdíl v našich argumentech. Vy argumentujete „je to špatně, protože já mám radši něco jiného“. Já argumentuju „tenhle přístup má tyhle výhody a tyhle nevýhody, výhodné je to pro tyhle případy; opačný přístup má tyhle výhody a tyhle nevýhody, výhodné je to pro tyhle případy“. Podle mne ten, kdo zabedněně lpí jenom na svém přístupu a považuje ho za jediný správný, jste tu vy.
Takdy ale vůbec nejde o to, co preferuje Filip Jirsák nebo někdo jiný. Důležité je, co očekává běžný Franta programátor od operátoru ==. Bežný Franta programátor očekává, že to bude porovnávat hodnoty a ne reference a už vůbec neočekává, že se to bude chovat pokaždé jinak podle kontextu. Proto je design operátoru == v Javě špatný, jednou to porovnává hodnoty, podruhé reference a běžný Franta programátor je z toho zmatený. Je úplně jedno, že Filipu Jirsákovi je křišťálově jasné, co to kdy dělá. Běžný Franta programátor v tom má hokej a dělá kvůli tomu zbytečné chyby.
V oborech, kde se pracuje s vážně s bezpečností, existuje pojem lidský činitel. Člověk je totiž ten nejslabší článek řetězu, který způsobuje nejvíce chyb. Když chceme chyby minimalizovat, musíme přizpůsobit design okolních systémů tak, aby s nimi člověk dokázal co nejlépe a nejintuitivněji pracovat. Chování operátoru == v Javě je podobné, jako kdyby někdo vyrobil letadlo, kde bude výškoměr v první polovině stupnice cejchovaný v metrech a v druhé polovině ve stopách. Samozřejmě by s tím šlo létat (a jsem si jistý, že Filip Jirsák bych to bravurně zvládal), ale u normálních pilotů by to způsobovalo zmatek a zbytečné chyby.
Tady docela hezké počtení k zamyšlení, proč by se měl design systému (programovacího jazyka) přizpůsobit člověku a ne opačně: http://projekt150.ha-vel.cz/node/117
-
Python 3 je zrovna příklad toho, jak problematické je to rozbití zpětné kompatibility – teprve teď se reálně daří zbavovat Pythonu 2. Perl na tohle rozbití zpětné kompatibility dokonce prakticky umřel, resp. odešel do zapomnění – dříve byla dvojice Perl a Python, která spolu soupeřila, Python spíš Perl doháněl. Dneska o Perlu neslyšíte.
Tohle je zajímavé téma, ale IMO spíš pro psychology a sociology. Zatímco v Pythonu core vývojáři jasně deklarovali, že Python 2 umře a ví se kdy, v Perlu došlo k tomu, že se z jednoho jazyka, už tak lehce obskurního, staly dva, které si vzájemně konkurují. Což přesně odpovídá filosofii Perlu - čím víc možností, tím líp. Python šel cestou největšího možného zjednodušení.
Navíc by mě zajímalo, v čem podle Tebe dělá Python == jako Java. Dělá to stejně jenom u "hloupých" objektů, chytrý objekt si umí říct, jestli jeho vnitřek odpovídá druhému objektu.
Tak si znovu přečtěte, co jsem napsal. Python ve výchozím stavu porovnává identitu objektů, ale je možné operátor přetížit.
No ale to je rozdíl. Na identitu má "is", což sémanticky přesně sedí a porovnávání operátorem == (pokud to jenom trošku jde) skutečně dělá co má. Zrovna tak tomu je i u < apod. Žádné compareTo() a podobná zvěrstva se nekonají.
-
Navíc by mě zajímalo, v čem podle Tebe dělá Python == jako Java. Dělá to stejně jenom u "hloupých" objektů, chytrý objekt si umí říct, jestli jeho vnitřek odpovídá druhému objektu.
Tak si znovu přečtěte, co jsem napsal. Python ve výchozím stavu porovnává identitu objektů, ale je možné operátor přetížit.
No ale to je rozdíl. Na identitu má "is", což sémanticky přesně sedí a porovnávání operátorem == (pokud to jenom trošku jde) skutečně dělá co má. Zrovna tak tomu je i u < apod. Žádné compareTo() a podobná zvěrstva se nekonají.
defaultne == dela to stejne co is.
-
V této diskusi ovšem máme jiný problém – někomu něco vyhovuje, a myslí si, že to tedy musí být jediné správně řešení a vše ostatní je špatně. Všimněte si na rozdíl v našich argumentech. Vy argumentujete „je to špatně, protože já mám radši něco jiného“. Já argumentuju „tenhle přístup má tyhle výhody a tyhle nevýhody, výhodné je to pro tyhle případy; opačný přístup má tyhle výhody a tyhle nevýhody, výhodné je to pro tyhle případy“. Podle mne ten, kdo zabedněně lpí jenom na svém přístupu a považuje ho za jediný správný, jste tu vy.
Takdy ale vůbec nejde o to, co preferuje Filip Jirsák nebo někdo jiný. Důležité je, co očekává běžný Franta programátor od operátoru ==. Bežný Franta programátor očekává, že to bude porovnávat hodnoty a ne reference a už vůbec neočekává, že se to bude chovat pokaždé jinak podle kontextu. Proto je design operátoru == v Javě špatný, jednou to porovnává hodnoty, podruhé reference a běžný Franta programátor je z toho zmatený. Je úplně jedno, že Filipu Jirsákovi je křišťálově jasné, co to kdy dělá. Běžný Franta programátor v tom má hokej a dělá kvůli tomu zbytečné chyby.
To máš podloženo nějakým průzkumem? Klidně to může být tak, že běžný programátor od jednoduchého jazyka očekává, že == vezme to co má nejvíc po ruce a porovná to a nebude dělat žádnou magii. Když si uvědomíš, že reference je hodnota pointeru, tak to v obou případech dělá to samé - porovnává hodnoty. A možná je to přesně to, co od toho očekává běžný programátor.
-
No ale to je rozdíl. Na identitu má "is", což sémanticky přesně sedí a porovnávání operátorem == (pokud to jenom trošku jde) skutečně dělá co má. Zrovna tak tomu je i u < apod. Žádné compareTo() a podobná zvěrstva se nekonají.
defaultne == dela to stejne co is.
A v čem se mnou nesouhlasíš?
-
operátor == se z definice nechová špatně. Špatně je, když jazyk dovolí napsat:
Integer a;
Integer b;
.... hodně kódu...
a = 1024;
b = 1024;
if(a == b){
...
}
Programátor vidící poslední 3 řádky na první pohled považuje proměnné a a b za primitivní číselné typy a očekává porovnávání hdnot a tedy shodu. Nemá šanci na pohled poznat, že jsou to ve skutečnosti objekty a že bude porovnávat reference a ne hodnoty.
Upozornění v IDE, že porovnává objekty ho může zachránit, ale ne každý editor kódu to umí.
-
Upozornění v IDE, že porovnává objekty ho může zachránit, ale ne každý editor kódu to umí.
Hlavně IDE by neměl být narovnávák na vohýbáky v jazyce.
-
To máš podloženo nějakým průzkumem? Klidně to může být tak, že běžný programátor od jednoduchého jazyka očekává, že == vezme to co má nejvíc po ruce a porovná to a nebude dělat žádnou magii. Když si uvědomíš, že reference je hodnota pointeru, tak to v obou případech dělá to samé - porovnává hodnoty. A možná je to přesně to, co od toho očekává běžný programátor.
"to co má nejvíc po ruce" je hodně neurčitý pojem. Může záviset na implementačních detailech o kterých by běžný programátor neměl ani vědět. Nebo by o nich minimálně neměl být nucený přemýšlet.
Má ten jazyk být jednoduchý na použití, nebo na implementaci? Tyhle dva požadavky jdou trochu proti sobě.
-
Takdy ale vůbec nejde o to, co preferuje Filip Jirsák nebo někdo jiný. Důležité je, co očekává běžný Franta programátor od operátoru ==. Bežný Franta programátor očekává, že to bude porovnávat hodnoty a ne reference a už vůbec neočekává, že se to bude chovat pokaždé jinak podle kontextu.
Pokud běžný Franta programátor očekává něco, co nesplňuje snad žádný programovací jazyk, není programátor. Nebo mi ukažte jazyk, který (alespoň ve výchozím nastavení, tj. bez přetěžování operátorů) porovnává vše podle hodnoty.
běžný Franta programátor je z toho zmatený
Nikoli, programátoři z toho zmatení nejsou a chyby v tom nedělají. Programováním se živím už spoustu let, a že se někdo pokusil v Javě porovnat Stringy pomocí == jsem viděl jednou, v kódu, který ještě ani nebyl commitnutý.
V oborech, kde se pracuje s vážně s bezpečností, existuje pojem lidský činitel. Člověk je totiž ten nejslabší článek řetězu, který způsobuje nejvíce chyb. Když chceme chyby minimalizovat, musíme přizpůsobit design okolních systémů tak, aby s nimi člověk dokázal co nejlépe a nejintuitivněji pracovat. Chování operátoru == v Javě je podobné, jako kdyby někdo vyrobil letadlo, kde bude výškoměr v první polovině stupnice cejchovaný v metrech a v druhé polovině ve stopách. Samozřejmě by s tím šlo létat (a jsem si jistý, že Filip Jirsák bych to bravurně zvládal), ale u normálních pilotů by to způsobovalo zmatek a zbytečné chyby.
Tady docela hezké počtení k zamyšlení, proč by se měl design systému (programovacího jazyka) přizpůsobit člověku a ne opačně: http://projekt150.ha-vel.cz/node/117
S tím, že se má design systému přizpůsobovat člověku a ne opačně, mi mluvíte z duše, propaguju to kudy chodím. Java se k tomu principu postavila tak, že vychází z několika málo jednoduchých principů, které programátor snadno pochopí. Rozdíl mezi primitivními typy a objekty programátor pochopí snadno, a že == u primitivních typů porovnává hodnotu (protože není co jiného porovnávat) a u objektů porovnává referenci, zatímco pro porovnání hodnot slouží metoda equals(), se naučí asi tak v druhé lekci.
Že neprogramátorům vadí chování operátoru == je mi celkem jedno, moc nechápu, proč to řeší. Že to kritizují jenom u Javy, i když úplně stejně to má C, C++, JavaScript, Python a mnoho dalších jazyků (přičemž jen některé umožňují operátor přetížit), to jenom ilustruje úroveň znalostí.
Programátor vidící poslední 3 řádky na první pohled považuje proměnné a a b za primitivní číselné typy a očekává porovnávání hdnot a tedy shodu. Nemá šanci na pohled poznat, že jsou to ve skutečnosti objekty a že bude porovnávat reference a ne hodnoty.
Upozornění v IDE, že porovnává objekty ho může zachránit, ale ne každý editor kódu to umí.
Ano, lze napsat kód prasácky, a když to pak jiné prase čte a očekává něco, co si neověří, skončí to nejspíš chybným kódem. IDE v tomto případě nepomůže, protože programátoři, kteří vyrábí takovýhle kód, varování ignorují.
Mimochodem, představte si, že v tom vašem příkladu a bude float a b bude double. A teď si ještě představte, že ten kód bude C. Nebo C++. To máme ale na světě nepoužitelných jazyků, že? A co je v nich napsáno aplikací…
-
Že neprogramátorům vadí chování operátoru == je mi celkem jedno, moc nechápu, proč to řeší. Že to kritizují jenom u Javy, i když úplně stejně to má C, C++, JavaScript, Python a mnoho dalších jazyků (přičemž jen některé umožňují operátor přetížit), to jenom ilustruje úroveň znalostí.
Teď jsem to celé pochopil, naše nedorozumění spočívalo v tom, že já jsem pojem programátor chápal v nějakém smyslu a Ty v jiném (programátor = spokojený javista). Tudíž nemá smysl se o to dál hádat, neb coby člověk, kterého živí hlavně Python (a předtím C++ a C), nemůžu účinně argumentovat.
Akorát bazíruju na tom, že s tím Pythonem jsi nadále mimo. Rozvádět spor do dalších jazyků nemá smysl, akorát dodám, že JavaScriptem bych se fakt neoháněl, protože to je bastl z definice (už jenom ta jejich vnitřní reprezentace čísel - tfuj!).
-
Nebo mi ukažte jazyk, který (alespoň ve výchozím nastavení, tj. bez přetěžování operátorů) porovnává vše podle hodnoty.
Rust. Reference porovnávat nelze, vždy se porovnávájí hodnoty. Pokud chci porovnat reference, musím je explicitně přetypovat na raw pointer.
Nikoli, programátoři z toho zmatení nejsou a chyby v tom nedělají. Programováním se živím už spoustu let, a že se někdo pokusil v Javě porovnat Stringy pomocí == jsem viděl jednou, v kódu, který ještě ani nebyl commitnutý.
Tak to jsi toho asi ještě viděl málo. Namátkou: https://github.com/swagger-api/swagger-codegen/commit/0d14496bd6fbdbd763844f239b34e198459fe4d9
Že neprogramátorům vadí chování operátoru == je mi celkem jedno, moc nechápu, proč to řeší. Že to kritizují jenom u Javy, i když úplně stejně to má C, C++, JavaScript, Python a mnoho dalších jazyků (přičemž jen některé umožňují operátor přetížit), to jenom ilustruje úroveň znalostí.
I to zastaralé C++, které má k dokonalosti hodně daleko, to má udělané líp než Java. Reference porovnávají hodnoty, pointery taky porovnávají hodnoty pointerů. Takový zmatečný kód, jako v Javě:
Integer a;
Integer b;
.... hodně kódu...
a = 1024;
b = 1024;
if(a == b){
...
}
nejde v C++ vyrobit, přiřazení konstanty do pointeru skončí chybou při kompilaci.
-
Takdy ale vůbec nejde o to, co preferuje Filip Jirsák nebo někdo jiný. Důležité je, co očekává běžný Franta programátor od operátoru ==. Bežný Franta programátor očekává, že to bude porovnávat hodnoty a ne reference a už vůbec neočekává, že se to bude chovat pokaždé jinak podle kontextu.
Pokud běžný Franta programátor očekává něco, co nesplňuje snad žádný programovací jazyk, není programátor. Nebo mi ukažte jazyk, který (alespoň ve výchozím nastavení, tj. bez přetěžování operátorů) porovnává vše podle hodnoty.
Žádný? ::) Třeba takový Haskell to umí porovnávání podle hodnoty generovat hodně pěkně. A i to pitomé C++ kód nezkompiluje, pokud autor knihovny ten operátor porovnání nevytvoří. Ano, mluvím o porovnávání podle hodnoty psaném programátorem. O porovnávání podle hodnoty ve vychozím nastavení tu totiž kromě vás nemluví vůbec nikdo.
Že neprogramátorům vadí chování operátoru == je mi celkem jedno, moc nechápu, proč to řeší. Že to kritizují jenom u Javy, i když úplně stejně to má C, C++, JavaScript, Python a mnoho dalších jazyků (přičemž jen některé umožňují operátor přetížit), to jenom ilustruje úroveň znalostí.
Už jsem to tu minimálně jednou psal, ale C++ to tak vážně nemá. Automaticky žádné operátory porovnávání negeneruje. Ten operátor porovnávání musí vždycky vytvořit autor knihovny.
Aby se v C++ vůbec dala porovnat identita objektů, musím na ně získat ukazatele a porovnat hodnoty těch ukazatelů. Tohle funguje úplně jinak než v Javě, kde objekt nemůže existovat bez ukazatele na něj. V C++ na spoustu objektů ukazatele nemám a nepotřebuju.
Slovo ukazatel místo slova reference jsem použil záměrně. Reference v C++ fungují o dost jinak než reference v Javě. Ty jazyky se liší dost fundamentálně.
-
Třeba ty zmíněné atomy v Erlangu jsou taky vnitřně uložené jako integery (takže obdoba ukazatelů), ale je jasně zadefinované, že když vytvářím atom třeba ze stringu, tak buďto v "cachi"/"poolu" už je, potom se ten integer zaručeně použije, nebo tam není, tak se vytvoří a pro příští volání se stejnou hodnotou bude platit první varianta.
Nebo jednou větou: podle mě by to singleton buď měl nebo neměl být z definice.
Já si soukromě myslím, že číslo by singleton být měla. A nejenom číslo.
Mám číslo nebo objekt. Obvykle s ním pracuju jako s hodnotou. (To, že neustálé kopírování je drahé je mi jasné. Ale to je technickej detail, a dá se to řešit.) Ale někdy chci někam poslat objekt (nebo číslo), a chci, aby mi ho dotyčný změnil. Takže tam pošlu referenci. Referenci na to číslo nebo objekt.
Ano, takto to u ideální jazyka chci.
Hezky to má Rust. Použitelně to má Python (== versus is). V případě Javy je to pochybení, ale tak je to historie, nemusíme je v tom máchat.
-
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.
Nemohu souhlasit. Stačí se na to dívat tak, že porovnáváte hodnotu versus identitu, a není problém.
Co naopak problém je, jsou technické/výkonnostní potíže. Ostatně proto, se soukromě domnívám, ty objekty jsou řešené jako reference.
No a problem je, ze v jave operator == neporovnava ani identitu ani hodnotu. On teda se snazi porovnavat identitu, ale z navrhu jinych casti jazyka (napr cachovani hodnot Integeru) zpusobuje, ze neresi ani identitu, takze ten operator ma v podstate nedeterministicke chovani(protoze specka rika jen minimalni pocet cachovanych hodnot).
-
co nesplňuje snad žádný programovací jazyk, není programátor. Nebo mi ukažte jazyk, který (alespoň ve výchozím nastavení, tj. bez přetěžování operátorů) porovnává vše podle hodnoty.
Prakticky kazdy funkcionalni jazyk? Prakticky kazdy jazyk se strukturalnim typovanim?
Samozrejme bez "neceho jako pretypovani" to nejde, respektive by to nebylo uplne prakticke - sam jste psal, ze relaci ekvivalence je hodne, takze pri definici typu se proste dost hodi rict, kterou z nich chci :) Jak psal kolega: obvykle to ani nemusim psat, nejtypictejsi pripad si umi prekladac snadno odvodit (porovnani po slozkach).
Nikoli, programátoři z toho zmatení nejsou a chyby v tom nedělají. Programováním se živím už spoustu let, a že se někdo pokusil v Javě porovnat Stringy pomocí == jsem viděl jednou, v kódu, který ještě ani nebyl commitnutý.
Pekna. A mate rad i tu o Budulinkovi?
(https://i.ibb.co/JCNG69Y/java.png)
Že neprogramátorům vadí chování operátoru ==
Konecne jsem to pochopil, vy jste tady jediny programator a vsichni ostatni jsou...
asi lopaty, co?
, moc nechápu, proč to řeší.
Jo, to je videt, protoze porad melete ty same veci dokola a vubec nereflektujete, co vam tady lidi rikaji.
úplně stejně to má C, C++, JavaScript, Python a mnoho dalších jazyků (přičemž jen některé umožňují operátor přetížit)
To je jako byste napsal, ze ze Lojza ma jednu ruku a uplne vsichni ostatni lidi jsou na tom stejne, akorat nekteri z nich maji dve.
OMG!
, to jenom ilustruje úroveň znalostí.
Uz chapu, proc se za Javu tolik plati. Odpovida to urovni ega.
Ano, lze napsat kód prasácky, a když to pak jiné prase čte a očekává něco, co si neověří,
Nedej matko prirodo, aby si to ten ubohy nedouk dokonce vyzkousel - akorat na tzv. "malych cislech" :)
Mimochodem, představte si, že v tom vašem příkladu a bude float a b bude double.
Predstavoval jsem si to tak silne, az se mi to zhmotnilo: https://play.golang.org/p/Jw9NlBT_VC_1
-
V případě Javy je to pochybení, ale tak je to historie, nemusíme je v tom máchat.
Jedinej, kdo se tady v tom macha, je Filip. Urcite to neni jediny zdejsi javista. Kdyby neobhajoval neobhajitelne pruhlednymi demagogiemi a rekl "jasny hosi, tohle muze nekoho zmast, ale jinak je ta Java stejne docela fajn, ne?", nerekl by nikdo ani popel.
-
Nemá vliv na návratové hodnoty operátoru. Operátor == v Javě při použití na objekty porovnává reference. Integer ani String nejsou definovány jako singletony, takže v běžící aplikaci může existovat víc instancí, které shodou okolností obsahují stejnou hodnotu. Pokud tedy operátorem == porovnávám reference ukazující na stejnou hodnotu, je věcí náhody, zda dostanu true nebo false. Pool opakovaně používaných instancí akorát zvyšuje pravděpodobnost, že se pro stejnou hodnotu použijí stejné instance, ale nikdo nemůže zaručit, že nevznikne jiná instance se stejnou hodnotou.
Ne, tohle neni nemusi byt zadna hruba chyba a doufam, ze tyhle nesmysly nevykladate juniorum. Je presne definovano, kdy se to rovnat musi a je to i zduvodnene. Naopak idealne by se chtelo, aby se to rovnalo, ale z performance duvodu to neni vyzadovane v celem oboru platnosti danych typu. Rikate to presne opacne nez to je.
If the value p being boxed is an integer literal of type int between -128 and 127 inclusive (§3.10.1), or the boolean literal true or false (§3.10.3), or a character literal between '\u0000' and '\u007f' inclusive (§3.10.4), then let a and b be the results of any two boxing conversions of p. It is always the case that a == b.
Ideally, boxing a primitive value would always yield an identical reference. In practice, this may not be feasible using existing implementation techniques. The rule above is a pragmatic compromise, requiring that certain common values always be boxed into indistinguishable objects. The implementation may cache these, lazily or eagerly. For other values, the rule disallows any assumptions about the identity of the boxed values on the programmer's part. This allows (but does not require) sharing of some or all of these references. Notice that integer literals of type long are allowed, but not required, to be shared.
This ensures that in most common cases, the behavior will be the desired one, without imposing an undue performance penalty, especially on small devices. Less memory-limited implementations might, for example, cache all char and short values, as well as int and long values in the range of -32K to +32K.
https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.7 (https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.7)
A to je pekny priklad neintuitivnosti jazyka.
-
V případě Javy je to pochybení, ale tak je to historie, nemusíme je v tom máchat.
Jedinej, kdo se tady v tom macha, je Filip. Urcite to neni jediny zdejsi javista. Kdyby neobhajoval neobhajitelne pruhlednymi demagogiemi a rekl "jasny hosi, tohle muze nekoho zmast, ale jinak je ta Java stejne docela fajn, ne?", nerekl by nikdo ani popel.
Já vím 8)
-
Mimochodem, představte si, že v tom vašem příkladu a bude float a b bude double.
Predstavoval jsem si to tak silne, az se mi to zhmotnilo: https://play.golang.org/p/Jw9NlBT_VC_1
+1
-
Nikoli, programátoři z toho zmatení nejsou a chyby v tom nedělají. Programováním se živím už spoustu let, a že se někdo pokusil v Javě porovnat Stringy pomocí == jsem viděl jednou, v kódu, který ještě ani nebyl commitnutý.
Tak to jste toho viděl dost málo...
Že neprogramátorům vadí chování operátoru == je mi celkem jedno, moc nechápu, proč to řeší. Že to kritizují jenom u Javy, i když úplně stejně to má C, C++, JavaScript, Python a mnoho dalších jazyků (přičemž jen některé umožňují operátor přetížit), to jenom ilustruje úroveň znalostí.
A co když to kritizují všude? :D
Mimochodem, představte si, že v tom vašem příkladu a bude float a b bude double. A teď si ještě představte, že ten kód bude C. Nebo C++. To máme ale na světě nepoužitelných jazyků, že? A co je v nich napsáno aplikací…
Představil jsem si to a.. kde je problém? Ono se to bude chovat jinak při hodnotách "dostatečně malých" a "dostatečně velkých"?
-
If the value p being boxed is an integer literal of type int between -128 and 127 inclusive (§3.10.1), or the boolean literal true or false (§3.10.3), or a character literal between '\u0000' and '\u007f' inclusive (§3.10.4), then let a and b be the results of any two boxing conversions of p. It is always the case that a == b.
To je uz skoro jak == v JS :)))
Jinak ale diky za vecne upresneni. Konecne teda misto kecu overitelna fakta.
-
Konecne jsem to pochopil, vy jste tady jediny programator a vsichni ostatni jsou...
asi lopaty, co?
Ale no tak... ::)
-
operátor == se z definice nechová špatně. Špatně je, když jazyk dovolí napsat:
Integer a;
Integer b;
.... hodně kódu...
a = 1024;
b = 1024;
if(a == b){
...
}
Programátor vidící poslední 3 řádky na první pohled považuje proměnné a a b za primitivní číselné typy a očekává porovnávání hdnot a tedy shodu. Nemá šanci na pohled poznat, že jsou to ve skutečnosti objekty a že bude porovnávat reference a ne hodnoty.
Upozornění v IDE, že porovnává objekty ho může zachránit, ale ne každý editor kódu to umí.
K tomu přece vůbec není potřebné IDE. Když je někdo čuně...
-
If the value p being boxed is an integer literal of type int between -128 and 127 inclusive (§3.10.1), or the boolean literal true or false (§3.10.3), or a character literal between '\u0000' and '\u007f' inclusive (§3.10.4), then let a and b be the results of any two boxing conversions of p. It is always the case that a == b.
To je uz skoro jak == v JS :)))
Presne :) A v dalsim zduvodneni vyslovene pisou ze zamer je aby sly boxovane hodnoty co nejvic porovnavat operatorem ==. Akorat se jim to zdalo moc narocne, tak dle normy staci jen cast oboru hodnot takhle porovnat.. .. mi to prijde jako docela komedie teda...
-
Presne :) A v dalsim zduvodneni vyslovene pisou ze zamer je aby sly boxovane hodnoty co nejvic porovnavat operatorem ==. Akorat se jim to zdalo moc narocne, tak dle normy staci jen cast oboru hodnot takhle porovnat.. .. mi to prijde jako docela komedie teda...
Řád zlatého šálku v akci. Když jsem psal ten úvodní vstup, chtěl jsem si text zcenzurovat jako příliš tvrdý.
-
I to zastaralé C++, které má k dokonalosti hodně daleko, to má udělané líp než Java. Reference porovnávají hodnoty, pointery taky porovnávají hodnoty pointerů. Takový zmatečný kód, jako v Javě:
Integer a;
Integer b;
.... hodně kódu...
a = 1024;
b = 1024;
if(a == b){
...
}
nejde v C++ vyrobit, přiřazení konstanty do pointeru skončí chybou při kompilaci.
Ono to nejde vyrobit ani v té Javě.
-
Ono to nejde vyrobit ani v té Javě.
Ne? Já ten kus kódu zkusil v https://www.jdoodle.com/online-java-compiler a prošlo mi to.
-
I to zastaralé C++, které má k dokonalosti hodně daleko, to má udělané líp než Java. Reference porovnávají hodnoty, pointery taky porovnávají hodnoty pointerů. Takový zmatečný kód, jako v Javě:
Integer a;
Integer b;
.... hodně kódu...
a = 1024;
b = 1024;
if(a == b){
...
}
nejde v C++ vyrobit, přiřazení konstanty do pointeru skončí chybou při kompilaci.
Ono to nejde vyrobit ani v té Javě.
Cože? https://onlinegdb.com/rJiGixihN
-
Ono to nejde vyrobit ani v té Javě.
Ne? Já ten kus kódu zkusil v https://www.jdoodle.com/online-java-compiler a prošlo mi to.
Však na té stránce je to dobře.
-
Teď jsem to celé pochopil, naše nedorozumění spočívalo v tom, že já jsem pojem programátor chápal v nějakém smyslu a Ty v jiném (programátor = spokojený javista). Tudíž nemá smysl se o to dál hádat, neb coby člověk, kterého živí hlavně Python (a předtím C++ a C), nemůžu účinně argumentovat.
Ne, zase jste to nepochopil. Por mne programátor je někdo, kdo ovládá alespoň základy jazyka, ve kterém chce programovat.
Akorát bazíruju na tom, že s tím Pythonem jsi nadále mimo.
Bazírovat na tom můžete, smutné je, že nemáte pravdu.
class Integer:
def __init__(self, value):
self.value = value
a = Integer(1024)
b = Integer(1024)
print(a == b) #False
Co jsem udělal? Vzal jsem ten původní kód, který rozpoutal celou debatu. Kód, který se údajně v Javě chová úplně nemožně. Napsal jsem to samé v Pythonu, který se prý chová úplně jinak. A výsledek je co? Výsledek je naprosto stejný. Python se chová přesně tak, jako se chová Java, dělá přesně to, co kritizujete.
Jistě budete argumentovat tím, že v Pythonu to můžete dál vylepšovat. Můžete do třídy dopsat přetížení operátoru:
def __eq__(self, other):
return self.value == other.value
Ano, můžete. V Javě to také můžete dál upravovat, můžete přetížit metodu equals() (resp. ve třídě java.lang.Integer už ta metoda dávno přetížená je).
Předpokládám, že budete argumentovat i tím, že žádný soudný Python programátor by nenapsal třídu Integer bez toho přetíženého operátoru. Jenže stejně tak by žádný soudný programátor v Javě neporovnával objekty pomocí ==, když chce porovnávat hodnoty.
Takže se zase dostáváme zpět k tomu, že jediný rozdíl mezi Javou a Pythonem v tomto případě je ten, že Java drží minimum funkcí samotného jazyka a proto nedovoluje přetěžovat operátory. Každý Java programátor to ví, takže ho ani nenapadne porovnávat objekty pomocí == a bude shánět metodu, která porovnání dělá. Zároveň každý Java programátor ví, že když dává smysl u jeho třídy zjišťovat ekvivalenci s jiným objektem, musí přetížit metodu equals(). Python naproti tomu chce mít bohatší jazyk, takže umožňuje přetěžovat operátory – a každý Python programátor ví, že když dává smysl porovnávat na ekvivalenci jeho třídu, musí přetížit metodu __eq__.
Jsou to dva různé přístupy, každý má svá pro a proti – a přístup, který vyhovuje vám osobně, proto ještě není obecně lepší.
akorát dodám, že JavaScriptem bych se fakt neoháněl, protože to je bastl z definice (už jenom ta jejich vnitřní reprezentace čísel - tfuj!).
To je právě ten problém, že vy se na to pořád díváte tak, že co vyhovuje vám, musí vyhovovat všem, a co vám nevyhovuje, nemůže vyhovovat nikomu. JavaScript vznikla za nějakým účelem, a pro daný účel je vnitřní reprezentace čísel úplně nezajímavá.
-
To je právě ten problém, že vy se na to pořád díváte tak, že co vyhovuje vám, musí vyhovovat všem, a co vám nevyhovuje, nemůže vyhovovat nikomu. JavaScript vznikla za nějakým účelem, a pro daný účel je vnitřní reprezentace čísel úplně nezajímavá.
Marxismus-leninismus taky vznikl v nejake dobe za nejakym ucelem a v te dobe a pro ten ucel to mohlo nekomu pripadat jako docela dobry napad.
-
I to zastaralé C++, které má k dokonalosti hodně daleko, to má udělané líp než Java. Reference porovnávají hodnoty, pointery taky porovnávají hodnoty pointerů. Takový zmatečný kód, jako v Javě:
Už tomu rozumím. Stačí prohlásit, že Java porovnává hodnoty referencí, a vše je v naprostém pořádku.
Můžete kličkovat jak chcete, ale z toho, že v C a C++ se u pointerů porovnávají přímo ty adresy, se nevykroutíte. A Javovské reference jsou jenom bezpečně udělané pointery, nic jiného.
-
Předpokládám, že budete argumentovat i tím, že žádný soudný Python programátor by nenapsal třídu Integer bez toho přetíženého operátoru. Jenže stejně tak by žádný soudný programátor v Javě neporovnával objekty pomocí ==, když chce porovnávat hodnoty.
Žádný soudný programátor v Pythonu tohle implementovat nemusí, protože Python umí libovolně velký integer vytvořit a příčetně porovnávat s jiným integerem. A samozřejmě pokud by to dělal a neimplementoval základní operace, bylo by to ... divné. Zbytek jsou debaty o ... zlatém šálku ... a to, s prominutím, nemám zapotřebí.
-
Jen jsem si cvičně zkusl totéž v c# (Web app, nechci si otvírat nový projekt) - pro mě se to tam chová logicky - když je něco číslo (i když vlastně vestavěný objekt), tak pomocí == porovnávám hodnoty:
Int32 a = new Int32();
Decimal b = new Decimal();
a = 999999999;
b = 999999999;
Response.Write("a <" + a.GetType() + "> = " + a.ToString() + "< br>");
Response.Write("b <" + b.GetType() + "> = " + b.ToString() + "< br>");
if (a == b)
{ Response.Write("operátor == : Shoda!< br>"); }
else
{ Response.Write("operátor == : Neshoda!< br>"); }
if (a.Equals(b))
{ Response.Write("metoda equals() : Shoda!< br>"); }
else
{ Response.Write("metoda equals() : Neshoda!< br>"); }
Výstup:
a <System.Int32> = 999999999
b <System.Decimal> = 999999999
operátor == : Shoda!
metoda equals() : Neshoda!
-
I to zastaralé C++, které má k dokonalosti hodně daleko, to má udělané líp než Java. Reference porovnávají hodnoty, pointery taky porovnávají hodnoty pointerů. Takový zmatečný kód, jako v Javě:
Integer a;
Integer b;
.... hodně kódu...
a = 1024;
b = 1024;
if(a == b){
...
}
nejde v C++ vyrobit, přiřazení konstanty do pointeru skončí chybou při kompilaci.
Ono to nejde vyrobit ani v té Javě.
Cože? https://onlinegdb.com/rJiGixihN
Prošlo to testem? Neprošlo. Takže to nejde.
-
Představil jsem si to a.. kde je problém? Ono se to bude chovat jinak při hodnotách "dostatečně malých" a "dostatečně velkých"?
Co je to zase za demagogii? Nikdy tady nebyla řeč o něčem, co by se chovalo jinak při malých hodnotách a jinak při velkých. Celou dobu je řeč jenom o tom, že někdo udělal začátečnickou chybu při použití operátoru ==. Někdo tu uváděl příklad s dlouhým kódem, programátor uvidí jen přiřazení a podle toho si odvodí (chybný) typ proměnných a následně použije chybný operátor. Jenom jsem uvedl příklad, kdy úplně tu stejnou chybu udělá programátor v C++.
-
I to zastaralé C++, které má k dokonalosti hodně daleko, to má udělané líp než Java. Reference porovnávají hodnoty, pointery taky porovnávají hodnoty pointerů. Takový zmatečný kód, jako v Javě:
Integer a;
Integer b;
.... hodně kódu...
a = 1024;
b = 1024;
if(a == b){
...
}
nejde v C++ vyrobit, přiřazení konstanty do pointeru skončí chybou při kompilaci.
Ono to nejde vyrobit ani v té Javě.
Cože? https://onlinegdb.com/rJiGixihN
Prošlo to testem? Neprošlo. Takže to nejde.
Ehm... On tím, že to nejde vyrobit, myslel že se ten kód ani nezkompiluje.
-
Pekna. A mate rad i tu o Budulinkovi?
(https://i.ibb.co/JCNG69Y/java.png)
Šest údajných „chyb“, z toho dvě jsou opravdu chyby, další je, že jsou programátoři z něčeho zmatení, a zbývající polovina jsou věci, které nejdou ani přeložit. Ještě tam chybí překlepy v názvech klíčových slov. Tohle je pohádka.
-
další je, že jsou programátoři z něčeho zmatení
Coz je presne to, o cem se tady celou dobu bavime.
-
Šest údajných „chyb“, z toho dvě jsou opravdu chyby, další je, že jsou programátoři z něčeho zmatení, a zbývající polovina jsou věci, které nejdou ani přeložit. Ještě tam chybí překlepy v názvech klíčových slov. Tohle je pohádka.
Pohádka? Spíš absurdní komedie říznutá špatným tripem. Furt se tu opakuje, že je to častá chyba, protože je to pro programátory matoucí. A vy teď napíšete že to není opravdová chyba, že jsou jenom programátoři zmatení. Kde seženu ten matroš? ::)
-
další je, že jsou programátoři z něčeho zmatení
Coz je presne to, o cem se tady celou dobu bavime.
Jediný zmatený tady je, kolega Jirsák odpustí, kolega Jirsák.
-
Už tomu rozumím. Stačí prohlásit, že Java porovnává hodnoty referencí, a vše je v naprostém pořádku.
Můžete kličkovat jak chcete, ale z toho, že v C a C++ se u pointerů porovnávají přímo ty adresy, se nevykroutíte. A Javovské reference jsou jenom bezpečně udělané pointery, nic jiného.
Mě nějaké demagogické slovíčkaření nebere. V C++ funguje porovnání referencí naprosto intuitivně, porovnává to objekty:
const int& a = 1024;
const int& b = 1024;
std::cout << (a == b); // true
Tu chybu s dlouhým kódem v C++ nelze vyrobit. Ale nechci řešit C++, je to zastaralý jazyk, ale i tak má operátor porovnání vyřešený kosmicky lépe než Java. Co třeba Rust, k tomu ses nevyjádřil? Ve srovnání s Rustem je teprve vidět, jak je design operátoru porovnání v Javě zastaralý, vedoucí k chybám v kódu a celkově neobhajitelný. Ale chápu, že lidé, kteří pořádně neumí nic jiného než Javu, to tak vnímat nemusí.
-
Ne, tohle neni nemusi byt zadna hruba chyba a doufam, ze tyhle nesmysly nevykladate juniorum. Je presne definovano, kdy se to rovnat musi a je to i zduvodnene. Naopak idealne by se chtelo, aby se to rovnalo, ale z performance duvodu to neni vyzadovane v celem oboru platnosti danych typu. Rikate to presne opacne nez to je.
Je to hrubá chyba. V Javě je opravdu přesně definováno, kdy operátor == při porovnání objektů vrací true – tehdy a jen tehdy, když se jedná o tytéž objekty (tj. stejné místo v paměti), nebo když jsou obě reference null.
String ani Integer nikdy nebyly v Javě definovány jako singletony, v žádném oboru hodnot. To, že mají vedle klasického konstruktoru také statické metody, které dělají jistou optimalizaci při vytváření instancí, na to nemá vliv.
To, co jste citoval, je popis toho, že pro určité hodnoty, u kterých se očekává časté použití, je udělaná optimalizace, aby se nevytvářely stále nové instance. Je to tedy čistě z výkonnostních důvodů, a ve specifikaci je to proto, aby bylo možné se na to spolehnout a někdo si neimplementoval vlastní pool instancí. V tom popisu ale není nic o tom, že se ty hodnoty mají porovnávat operátorem ==. Je tam jenom přesná specifikace, že v takových případech budou instance identické tehdy a jen tedy, pokud jsou identické i hodnoty. Vám to jistě připadá samozřejmé, ale specifikace má být přesná a popisovat i to, co se zdá samozřejmé.
-
Tu chybu s dlouhým kódem v C++ nelze vyrobit.
float a = 2147483647;
double b = 2147483647;
// dlouhý kód
std::cout << (a == b);
-
Tu chybu s dlouhým kódem v C++ nelze vyrobit.
float a = 2147483647;
double b = 2147483647;
// dlouhý kód
std::cout << (a == b);
Což ale dělá úplně něco jiného než porovnání referencí. To je typový problém. Hrušky a jabka.
-
Pohádka? Spíš absurdní komedie říznutá špatným tripem. Furt se tu opakuje, že je to častá chyba, protože je to pro programátory matoucí. A vy teď napíšete že to není opravdová chyba, že jsou jenom programátoři zmatení. Kde seženu ten matroš? ::)
Aby to bylo pro čtenáře jednodušší, vzal jsem ty body v tom pořadí, jak jsou na obrázku. 1. bod – skutečná chyba. 2. bod – programátoři jsou z něčeho zmatení. 3. bod – skutečná chyba a zároveň věc, kterou tu řešíme. Nic o zmatení u třetího bodu, který tu řešíme, jsem nepsal. Zpochybnil jsem ten seznam „nejčastějších chyb“ – když půlka věcí je něco, co ani nejde zkompilovat, těžko to vzniklo analýzou existujícího kódu. Takže to není žádný seznam získaný měřením, je to jen něčí dojem.
-
Tu chybu s dlouhým kódem v C++ nelze vyrobit.
float a = 2147483647;
double b = 2147483647;
// dlouhý kód
std::cout << (a == b);
Což ale dělá úplně něco jiného než porovnání referencí. To je typový problém. Hrušky a jabka.
A hlavně tady ta chyba není vůbec v tom porovnání. Hodnota toho čísla se změní už při tom přiřazení. Zásadní rozdíl vidím hlavně v tom, že pokud si bude začátečník krokovat tenhle kód v debuggeru, nebo si ty proměnné vypíše, tak ho to trkne.
-
Což ale dělá úplně něco jiného než porovnání referencí. To je typový problém. Hrušky a jabka.
V tom příkladu bylo uvedeno, že dlouhý kód může způsobit, že programátor použije chybný operátor a kompilátor mu v tom nezabrání. Tohle je přesně ten případ. Proč je použití toho operátoru chybné je jedno. Navíc oba dva případy jsou o tom, že programátor si myslel, že porovnává jeden typ (int), ale ve skutečnosti porovnával jiný typ. To je podstat toho příkladu. Jestli místo intu porovnává reference, float nebo double je úplně jedno, protože ve všech třech případech nelze pro porovnání hodnoty použít operátor == (i když v prvním případě z jiného důvodu než ve druhých dvou).
Obávám se, že jsem právě odstartoval další diskusi na 200 příspěvků, kde mne budou všichni přesvědčovat, že porovnávat floaty na rovnost je přece úplně normální.
-
V tom popisu ale není nic o tom, že se ty hodnoty mají porovnávat operátorem ==.
Mně už zbývá v hlavě jediná otázka: připomíná mi tohle spíš Havla nebo Goebbelse? Nějak se nemůžu rozhodnout.
-
Což ale dělá úplně něco jiného než porovnání referencí. To je typový problém. Hrušky a jabka.
V tom příkladu bylo uvedeno, že dlouhý kód může způsobit, že programátor použije chybný operátor a kompilátor mu v tom nezabrání. Tohle je přesně ten případ. Proč je použití toho operátoru chybné je jedno. Navíc oba dva případy jsou o tom, že programátor si myslel, že porovnává jeden typ (int), ale ve skutečnosti porovnával jiný typ. To je podstat toho příkladu. Jestli místo intu porovnává reference, float nebo double je úplně jedno, protože ve všech třech případech nelze pro porovnání hodnoty použít operátor == (i když v prvním případě z jiného důvodu než ve druhých dvou).
Obávám se, že jsem právě odstartoval další diskusi na 200 příspěvků, kde mne budou všichni přesvědčovat, že porovnávat floaty na rovnost je přece úplně normální.
WTF :o
-
Pohádka? Spíš absurdní komedie říznutá špatným tripem. Furt se tu opakuje, že je to častá chyba, protože je to pro programátory matoucí. A vy teď napíšete že to není opravdová chyba, že jsou jenom programátoři zmatení. Kde seženu ten matroš? ::)
Aby to bylo pro čtenáře jednodušší, vzal jsem ty body v tom pořadí, jak jsou na obrázku. 1. bod – skutečná chyba. 2. bod – programátoři jsou z něčeho zmatení. 3. bod – skutečná chyba a zároveň věc, kterou tu řešíme. Nic o zmatení u třetího bodu, který tu řešíme, jsem nepsal. Zpochybnil jsem ten seznam „nejčastějších chyb“ – když půlka věcí je něco, co ani nejde zkompilovat, těžko to vzniklo analýzou existujícího kódu. Takže to není žádný seznam získaný měřením, je to jen něčí dojem.
Proč by ten seznam musel vzniknout analýzou existujícího kódu? Stejně tak mohl vzniknout na základě pozorování bandy juniorů. Když začátečníci dělají pravidelně něco, kvůli čemu se jim kód nezkompiluje, tak to nejsou chyby? Co to teda je?
IMO, když to odchytí kompiler, tak to není zákeřná past. Ale chyba je to furt.
-
V tom příkladu bylo uvedeno, že dlouhý kód může způsobit, že programátor použije chybný operátor a kompilátor mu v tom nezabrání.
Já souhlasím, že je to problém. C++ umožnuje porovnávat float a double a přiřazovat do nich integery. Je to špatný design, C++ je zastaralé. Java je v tom špatném designu ještě dál než C++ a operátor == v Javě někdy porovnává hodnoty a jindy reference podle kontextu. Je to prostě blbě. Jak by to mělo vypadat správně?
let a : f32 = 2147483647;
let b : f64 = 2147483647;
println!("{}", a == b);
Rust mi řekně hned 3x, že dělám 3 prasárny a opravdu to není dobrý nápad:
error[E0308]: mismatched types
--> src/main.rs:2:20
|
2 | let a : f32 = 2147483647;
| ^^^^^^^^^^ expected f32, found integral variable
|
= note: expected type `f32`
found type `{integer}`
error[E0308]: mismatched types
--> src/main.rs:3:19
|
3 | let b : f64 = 2147483647;
| ^^^^^^^^^^ expected f64, found integral variable
|
= note: expected type `f64`
found type `{integer}`
error[E0308]: mismatched types
--> src/main.rs:4:25
|
4 | println!("{}", a == b);
| ^ expected f32, found f64
-
V tom popisu ale není nic o tom, že se ty hodnoty mají porovnávat operátorem ==.
Mně už zbývá v hlavě jediná otázka: připomíná mi tohle spíš Havla nebo Goebbelse? Nějak se nemůžu rozhodnout.
Já vám pomůžu – zkuste si do češtiny přeložit tyhle dvě věty (uznávám, je to docela dlouhé):
If the value p being boxed is an integer literal of type int between -128 and 127 inclusive (§3.10.1), or the boolean literal true or false (§3.10.3), or a character literal between '\u0000' and '\u007f' inclusive (§3.10.4), then let a and b be the results of any two boxing conversions of p. It is always the case that a == b.
Bude fajn, když se o ten překlad podělíte s ostatními, abychom věděli, o čem se tady podle vás bavíme.
Nebo samozřejmě můžete v té citaci zkusit najít jinou část, kde se mluví o operátoru ==.
-
Java je v tom špatném designu ještě dál než C++ a operátor == v Javě někdy porovnává hodnoty a jindy reference podle kontextu. Je to prostě blbě.
Ano, Java opravdu porovnává podle kontextu. Když dostane hodnoty, porovnává hodnoty. Když dostane reference, porovnává reference. Stejně jako C, které když dostane hodnoty, porovnává hodnoty, když dostane pointery, porovnává pointery, a když dostane hodnotu a pointer, klidně je také porovná. Když Java dostane hodnotu a referenci, kompilátor zařve. Pravda, poslední věc se změnila s i implementaci autoboxingu/unboxingu, kdy kompilátor nejprve zkusí provést autoboxing/unboxing a zařve teprve, když to není možné.
Takže je chyba v autoboxingu/unboxingu? Než se zavedl, byly diskuse plné komentářů o tom, jak je Java zastaralá a ukecaná a jako příklad se často objevovalo právě převádění mezi „malými“ a „velkými“ typy. Takže se zavedl autoboxing a autounboxing. Jak byste to řešili vy? Zakázali boxing u operátoru ==? Rozbili zpětnou kompatibilitu a zakázali úplně operátor == pro reference? Nebo jinak?
Rust mi řekně hned 3x, že dělám 3 prasárny a opravdu to není dobrý nápad:
Po dlouhé době něco, s čím mohu souhlasit.
-
Stejně jako C, které když dostane hodnoty, porovnává hodnoty, když dostane pointery, porovnává pointery, a když dostane hodnotu a pointer, klidně je také porovná.
No jistě, protože porovnává adresu - hodnotu v pointeru. :D
Když dostane pointer a hodnotu, tak ti vynadá kompilátor.
-
Ano, Java opravdu porovnává podle kontextu. Když dostane hodnoty, porovnává hodnoty. Když dostane reference, porovnává reference. Stejně jako C, které když dostane hodnoty, porovnává hodnoty, když dostane pointery, porovnává pointery, a když dostane hodnotu a pointer, klidně je také porovná. Když Java dostane hodnotu a referenci, kompilátor zařve. Pravda, poslední věc se změnila s i implementaci autoboxingu/unboxingu, kdy kompilátor nejprve zkusí provést autoboxing/unboxing a zařve teprve, když to není možné.
Takže je chyba v autoboxingu/unboxingu? Než se zavedl, byly diskuse plné komentářů o tom, jak je Java zastaralá a ukecaná a jako příklad se často objevovalo právě převádění mezi „malými“ a „velkými“ typy. Takže se zavedl autoboxing a autounboxing. Jak byste to řešili vy? Zakázali boxing u operátoru ==? Rozbili zpětnou kompatibilitu a zakázali úplně operátor == pro reference? Nebo jinak?
Je potřeba dívat se na to v historickém kontextu. Java vypadá tak, jak vypadá, kvůli rozhodnutím z minulosti (primitivní typy, boxing/unboxing...) a držení zpětné kompatibility. To není kritika, byl to prostě historický výjoj. Kdyby se dělala Java znovu od nuly, určitě by vypadala jinak než teď. Stejně jako by vypadalo jinak i C, kdyby se dělalo znovu od nuly. Ale obojí je nereálné, Java i C jsou etablované jazyky a nová Java ani nové C nebude, existující kód nikdo přepisovat nechce.
Jenomže ten historický vývoj nutně vedl ke kompromisům v designu jazyka, Java (stejně jako C(++)) si s sebou táhne spoustu špatných věcí z minulosti. A tahkle je to taky potřeba vnímat, operátor == je v Javě neintuitivní a špatně použitelný, snadno vznikne chyba. Takže je potřeba každému nováčkovi říct "Hele, dávej si pozor na operátor ==, protože nefunguje tak, jak bys čekal, dělají se v tom často chyby. Někdy to porovnává hodnoty, jindy reference, záleží na kontextu, pravidla jsou složitá. Radši to vůbec nepoužívej, když nebudeš muset". No a to je všechno, není potřeba to nějak víc řešit.
-
No jistě, protože porovnává adresu - hodnotu v pointeru. :D
Stejně jako při porovnání referencí v Javě, kdy se také porovnává adresa – hodnota v referenci.
Když dostane pointer a hodnotu, tak ti vynadá kompilátor.
Nikoli.
-
Je potřeba dívat se na to v historickém kontextu. Java vypadá tak, jak vypadá, kvůli rozhodnutím z minulosti (primitivní typy, boxing/unboxing...) a držení zpětné kompatibility. To není kritika, byl to prostě historický výjoj. Kdyby se dělala Java znovu od nuly, určitě by vypadala jinak než teď. Stejně jako by vypadalo jinak i C, kdyby se dělalo znovu od nuly. Ale obojí je nereálné, Java i C jsou etablované jazyky a nová Java ani nové C nebude, existující kód nikdo přepisovat nechce.
Jenomže ten historický vývoj nutně vedl ke kompromisům v designu jazyka, Java (stejně jako C(++)) si s sebou táhne spoustu špatných věcí z minulosti. A tahkle je to taky potřeba vnímat, operátor == je v Javě neintuitivní a špatně použitelný, snadno vznikne chyba. Takže je potřeba každému nováčkovi říct "Hele, dávej si pozor na operátor ==, protože nefunguje tak, jak bys čekal, dělají se v tom často chyby. Někdy to porovnává hodnoty, jindy reference, záleží na kontextu, pravidla jsou složitá. Radši to vůbec nepoužívej, když nebudeš muset". No a to je všechno, není potřeba to nějak víc řešit.
Kdybyste si nalistoval diskusi asi tak pět stran zpět, psal jsem tam to samé :-) Teda až na to, že nesouhlasím, že pravidla jsou složitá – jsou naopak velmi jednoduchá, když se porovnávají primitivní typy, porovnávají se hodnoty (nic jiného porovnat nejde), když se porovnávají objektové typy, porovnávají se reference (protože objekt je pro Javu blackbox, neví nic o tom, jestli existuje něco jako hodnota toho objektu).
-
Kdybyste si nalistoval diskusi asi tak pět stran zpět, psal jsem tam to samé :-) Teda až na to, že nesouhlasím, že pravidla jsou složitá – jsou naopak velmi jednoduchá, když se porovnávají primitivní typy, porovnávají se hodnoty (nic jiného porovnat nejde), když se porovnávají objektové typy, porovnávají se reference (protože objekt je pro Javu blackbox, neví nic o tom, jestli existuje něco jako hodnota toho objektu).
Ne ty tady od začátku píšeš, jak je porovnání v Javě udělané skvěle, jednoduše, každý to hned pochopí a chyby se v tom nedělají. To je prostě demagogie, obhajuješ špatný design, pravděpodobně z neznalosti.
-
Když dostane pointer a hodnotu, tak ti vynadá kompilátor.
Nikoli.
-Wzero-as-null-pointer-constant
-
Bazírovat na tom můžete, smutné je, že nemáte pravdu.
class Integer:
def __init__(self, value):
self.value = value
a = Integer(1024)
b = Integer(1024)
print(a == b) #False
Co jsem udělal? Vzal jsem ten původní kód, který rozpoutal celou debatu. Kód, který se údajně v Javě chová úplně nemožně. Napsal jsem to samé v Pythonu, který se prý chová úplně jinak. A výsledek je co? Výsledek je naprosto stejný.
Python se chová přesně tak, jako se chová Java, dělá přesně to, co kritizujete.
Jistě budete argumentovat tím, že v Pythonu to můžete dál vylepšovat. Můžete do třídy dopsat přetížení operátoru:
def __eq__(self, other):
return self.value == other.value
Ano, můžete. V Javě to také můžete dál upravovat, můžete přetížit metodu equals() (resp. ve třídě java.lang.Integer už ta metoda dávno přetížená je).
Zase špatně: Výsledek není stejný. Python se nechová jako Java. https://gist.github.com/BoneFlute/6c9f56a6b4c164b2fc2f246662daa7a8
-
Když dostane pointer a hodnotu, tak ti vynadá kompilátor.
Nikoli.
Jak by řekl klasik: Tak to tady asi máte nějaký zkažený vzduch!
$ cat test.c
#include <stdio.h>
int main() {
int a;
int *ap;
if(ap != 1) {
puts("nerovna!");
}
if(ap != a) {
puts("nerovna!");
}
return 0;
}
$ gcc test.c
test.c:7:9: warning: comparison between pointer and integer ('int *' and 'int')
if(ap != 1) {
~~ ^ ~
test.c:11:9: warning: comparison between pointer and integer ('int *' and 'int')
if(ap != a) {
~~ ^ ~
2 warnings generated.
-
Ne ty tady od začátku píšeš, jak je porovnání v Javě udělané skvěle, jednoduše, každý to hned pochopí a chyby se v tom nedělají. To je prostě demagogie, obhajuješ špatný design, pravděpodobně z neznalosti.
Demagogie je vkládat mi do úst něco, co jsem nikdy nenapsal. Nikdy jsem nepsal, že je to skvělé. Napsal jsem, že je to jednoduché, a na tom trvám. Že to každý hned pochopí jsem nepsal – psal jsem, že je to jedna z prvních věcí, které se programátor v Javě musí naučit. Že se v tom chyby dělají málokdy jsem napsal a také na tom trvám.
Váš názor, že je to špatný design, vám neberu, ale nesdílím ho. Když vyjdu z předpokladů, že Java má primitivní typy (což je dané historicky a není reálné to v dohledné době změnit) a zároveň nemá přetěžování operátorů (což je záměr, přispívá to k tomu, aby jazyk jako takový byl jednoduchý a předvídatelný), vychází mi z toho, že o dost lepší řešení neexistuje. O něco lepší by bylo operátor == pro objekty úplně zakázat, ale to opět bylo v době vzniku Javy nemyslitelné.
Pokud máte lepší řešení, které splňuje výše uvedené předpoklady, sem s ním. Pokud si myslíte, že některý z těch předpokladů není nutné dodržet, můžete k tomu předložit nějaké argumenty.
-
Když dostane pointer a hodnotu, tak ti vynadá kompilátor.
Nikoli.
Jak by řekl klasik: Tak to tady asi máte nějaký zkažený vzduch!
„Vynadá“ jsem chápal tak, že to vůbec nepůjde přeložit. Protože když v Javě porovnáváte reference pomocí ==, také dostanete varování. Takže v tom rozdíl nevidím.
-
Zase špatně: Výsledek není stejný. Python se nechová jako Java. https://gist.github.com/BoneFlute/6c9f56a6b4c164b2fc2f246662daa7a8
Je fajn, že jste si to sám vyzkoušel, a zjistil jste, že je to přesně tak, jak jsem napsal. Akorát pak svůj komentář nezačínejte „zase špatně“, vypadá to, jako když jsem to napsal špatně já. Málokdo pochopí, jak jste to doopravdy myslel – že jste se zase mýlil vy.
-
Zase špatně: Výsledek není stejný. Python se nechová jako Java. https://gist.github.com/BoneFlute/6c9f56a6b4c164b2fc2f246662daa7a8
Je fajn, že jste si to sám vyzkoušel, a zjistil jste, že je to přesně tak, jak jsem napsal. Akorát pak svůj komentář nezačínejte „zase špatně“, vypadá to, jako když jsem to napsal špatně já. Málokdo pochopí, jak jste to doopravdy myslel – že jste se zase mýlil vy.
Hezkej pokus. Ale uvědomujete si, že si to každý nyní může prohlídnout a bude jim to jasné? 8)
-
Protože když v Javě porovnáváte reference pomocí ==, také dostanete varování. Takže v tom rozdíl nevidím.
Tak to mám asi nějaký zkažený vzduch já.
$ cat Main.java
public class Main {
public static void main(String[] args) {
Integer a = 1024;
Integer b = 1024;
System.out.print(a == b);
}
}
$ javac Main.java
$ java -cp . Main
false
-
Každopádně ale musím uznat, že je to řachanda.
$ cat Main.java
public class Main {
public static void main(String[] args) {
Integer a = 1024;
Integer b = 1024;
System.out.println(a == b);
System.out.println(a == 1024);
}
}
$ javac Main.java
$ java -cp . Main
false
true
Doporučují čtyři ze tří psychoterapeutů.
-
Každopádně ale musím uznat, že je to řachanda.
$ cat Main.java
public class Main {
public static void main(String[] args) {
Integer a = 1024;
Integer b = 1024;
System.out.println(a == b);
System.out.println(a == 1024);
}
}
$ javac Main.java
$ java -cp . Main
false
true
Doporučují čtyři ze tří psychoterapeutů.
(https://i.kym-cdn.com/photos/images/newsfeed/000/173/576/Wat8.jpg?1315930535)
Už zase nevím, která bije. :o Tohle bude nějaké další pravidlo toho jednoduchého a předvídatelného jazyka, které tu ještě nezaznělo.
-
Každopádně ale musím uznat, že je to řachanda.
$ cat Main.java
public class Main {
public static void main(String[] args) {
Integer a = 1024;
Integer b = 1024;
System.out.println(a == b);
System.out.println(a == 1024);
}
}
$ javac Main.java
$ java -cp . Main
false
true
Doporučují čtyři ze tří psychoterapeutů.
První problém: Nechceme použít přetěžování operátorů. Díky tomu nemůžeme chápat equals jako ==. To by problém řešilo.
Problém druhý: Je škoda nevyužít operátor ==, když už ho nemůžeme na objektech použít pro ekvivalenci. Tak ho použijeme na porovnání identity.
A je to :-) Myslím, že tak nějak to bylo.
-
Každopádně ale musím uznat, že je to řachanda.
$ cat Main.java
public class Main {
public static void main(String[] args) {
Integer a = 1024;
Integer b = 1024;
System.out.println(a == b);
System.out.println(a == 1024);
}
}
$ javac Main.java
$ java -cp . Main
false
true
Doporučují čtyři ze tří psychoterapeutů.
(https://i.kym-cdn.com/photos/images/newsfeed/000/173/576/Wat8.jpg?1315930535)
Už zase nevím, která bije. :o Tohle bude nějaké další pravidlo toho jednoduchého a předvídatelného jazyka, které tu ještě nezaznělo.
No auto-unboxing... to je podle me opravdu kontraintuitivni. To == mi nevadi, ale to krabickovani me fakt obcas rozciluje.
-
Tak to mám asi nějaký zkažený vzduch já.
Buď zkažený vzduch a nebo používáte jiný nástroj. Já málokdy překládám Javu z příkazového řádku, použil jsem to, co pro výpis varování používám normálně – IntelliJ Idea.
-
Buď zkažený vzduch a nebo používáte jiný nástroj. Já málokdy překládám Javu z příkazového řádku, použil jsem to, co pro výpis varování používám normálně – IntelliJ Idea.
Jo, v tom to bude. Já jsem použil Neintelligent Idea.
A proto je Java tak populární.
Tak populární, že polovina lidí, kteří v ní dělají, by v ní raději nedělali :)
-
Už zase nevím, která bije. :o Tohle bude nějaké další pravidlo toho jednoduchého a předvídatelného jazyka, které tu ještě nezaznělo.
Není to žádné další pravidlo. První porovnání – pokud jsou operandy operátoru == objektové typy, porovnávají se reference (ukazatele). Toto pravidlo už tu bylo zmíněné asi milionkrát. Druhé pravidlo, které už tu také bylo zmíněno, že když kompilátor narazí na aritmetický nebo porovnávací operátor který má takové operandy, že kód bez unboxingu nelze přeložit, zkusí provést unboxing. Opět je to velmi jednoduché pravidlo, zpětně kompatibilní změna – nová funkce (autounboxing) se použije pouze u kódu, který by dříve nebylo možné přeložit.
Mimochodem, řeší se tu pořád umělý příklad – myslím, že tahle diskuse zněkolikanásobila počet výskytů takovéhleho kódu v nám známém vesmíru.
No auto-unboxing... to je podle me opravdu kontraintuitivni. To == mi nevadi, ale to krabickovani me fakt obcas rozciluje.
Ano, někdy je otravný, ale lid si to žádal, prý v zájmu pokroku. Já to používám jedině u parametrů metod nebo při přiřazení návratové hodnoty metody do proměnné. Jakmile by k tomu auto- mělo dojít někde jinde, než přímo u deklarace proměnné nebo předání parametru do metody, dělám tam un/boxing ručně, aby to bylo na první pohled vidět a bylo zřejmé, že to dělám záměrně.
-
Tak to mám asi nějaký zkažený vzduch já.
Buď zkažený vzduch a nebo používáte jiný nástroj. Já málokdy překládám Javu z příkazového řádku, použil jsem to, co pro výpis varování používám normálně – IntelliJ Idea.
::) ::) ::) ::) ::)
-
No auto-unboxing... to je podle me opravdu kontraintuitivni. To == mi nevadi, ale to krabickovani me fakt obcas rozciluje.
Co mě tu hlavně mate je, že v tomhle případě by se mohlo jak automaticky boxovat tak unboxovat. A ani přečtení dokumentace mi neobjasnilo, co by mělo mít přednost. Jak boxování tak unboxování se má dělat v případě, že se předává parametr nebo přiřazuje do proměnné. Na základě čeho se rozhodne, že se tady má zrovna unboxovat?
-
Hezkej pokus. Ale uvědomujete si, že si to každý nyní může prohlídnout a bude jim to jasné? 8)
Mně to jasné je. Bavili jsme se o chování operátoru ==, takže jsem porovnal dva následující příklady (v historii diskuse to lze najít):
public class HelloWorld
{
public static void main(String[] args)
{
Integer a = 1024;
Integer b = 1024;
System.out.print(a == b); // false
}
}
class Integer:
def __init__(self, value):
self.value = value
a = Integer(1024)
b = Integer(1024)
print(a == b) #False
Vy jste si v Javě vyrobil vlastní typ Cislo a vyšlo vám to (jaké překvapení) stejně. Pro Python jste tenhle příklad vůbec nenapsal.
Dále jsem psal, že v Pythonu můžete přetížit operátor ==, Java přetěžování operátorů záměrně nepodporuje, ale pro porovnání hodnot objektů používá metodu equals(), kterou přetížit můžete. Psal jsem, že když to přetížení naimplementujete stejně v Pythonu i Javě, opět dostanete stejný výsledek. Kód pro Javu jsem tedy neuvedl, stačilo použít stávající implementaci v java.lang.Integer.equals()…
Tuhle variantu už jste vy naimplementoval pro oba jazyky, a – světe div se – je to jak jsem psal, vychází to v obou jazycích stejně. Dovolte, abych z vašeho kódu ty dva řádky vypíchl:
System.out.println(a.equals(b)); // true
print (a == b) # True – používá se zde přetížený operátor ==
Předpokládám, že teď budou následovat vaše klasické nářky, že jsem sice psal operátor ==, ale určitě jsem tím myslel identitu…
-
Co mě tu hlavně mate je, že v tomhle případě by se mohlo jak automaticky boxovat tak unboxovat. A ani přečtení dokumentace mi neobjasnilo, co by mělo mít přednost. Jak boxování tak unboxování se má dělat v případě, že se předává parametr nebo přiřazuje do proměnné. Na základě čeho se rozhodne, že se tady má zrovna unboxovat?
U aritmetických operátorů boxování nepřichází v úvahu, dostali bychom zase jen nepřeložitelný kód. No a u komparačních operátorů je za prvé rozumné dělat to stejně, za druhé by boxování bylo s prominutím padlé na hlavu, protože je čistě věcí kompilátoru, jakou referenci vám tam vrátí – a když ta reference mohla právě teď vzniknout, nedává smysl ji porovnávat na rovnost s jinou referencí. Porovnávat reference má smysl tam, kde už je máte z dřívějška uložené (třeba klasická zarážka při hledání v poli).
-
U aritmetických operátorů boxování nepřichází v úvahu, dostali bychom zase jen nepřeložitelný kód. No a u komparačních operátorů je za prvé rozumné dělat to stejně, za druhé by boxování bylo s prominutím padlé na hlavu, protože je čistě věcí kompilátoru, jakou referenci vám tam vrátí – a když ta reference mohla právě teď vzniknout, nedává smysl ji porovnávat na rovnost s jinou referencí. Porovnávat reference má smysl tam, kde už je máte z dřívějška uložené (třeba klasická zarážka při hledání v poli).
Tak už to konečně chápu naprosto kompletně!
Je to totiž Havel!
No tak ze začátku byl ten spor zajímavý, pak se ale trochu zvrhl, že?
Jistě, už od začátku měl takový nesprávný osobní tón, i když až do konce šlo o zajímavý a aktuální problém, ne?
Já vím, lze se na to dívat z různých úhlů, stran i pozic, vždycky je však nutné zvážit všechna pro i proti, nemám pravdu?
Oni vlastně oba měli tak trochu pravdu a oba se tak trochu mýlili – nebo vlastně naopak: oba se mýlili a oba měli pravdu, ne? Asi ano, ne?
Ano, také si myslím, že ne, i když si nemyslím, že ano. Oba totiž tak nějak zapomněli, že v budoucnosti se bude umění s technikou tak nějak harmonicky doplňovat – lyrickoepické verše pomohou při chemizaci likvidační praxe – periodická soustava prvků pomůže rozvoji impresionismu – na každém technickém výrobku bude zvláštní ploška, vyhrazená pro účinný estetický vjem – komíny atomových elektráren budou pomalovány našimi nejlepšími krajináři – dvacet tisíc mil pod mořem budou čítárny přístupné všem – diferenciální rovnice se budou psát ve verších – na střechách cyklotronů budou divadla malých forem – a v nich se budou recitovat diferenciální rovnice – tak nějak lidsky, ne?
Takže za mě: problem solved, mír s vámi, volové!
-
Co mě tu hlavně mate je, že v tomhle případě by se mohlo jak automaticky boxovat tak unboxovat. A ani přečtení dokumentace mi neobjasnilo, co by mělo mít přednost. Jak boxování tak unboxování se má dělat v případě, že se předává parametr nebo přiřazuje do proměnné. Na základě čeho se rozhodne, že se tady má zrovna unboxovat?
U aritmetických operátorů boxování nepřichází v úvahu, dostali bychom zase jen nepřeložitelný kód. No a u komparačních operátorů je za prvé rozumné dělat to stejně, a zda druhé by boxování bylo s prominutím padlé na hlavu, protože je čistě věcí kompilátoru, jakou referenci vám tam vrátí – a když ta reference mohla právě teď vzniknout, nedává smysl ji porovnávat na rovnost s jinou referencí. Porovnávat reference má smysl tam, kde už je máte z dřívějška uložené (třeba klasická zarážka při hledání v poli).
Takže je to speciální ad-hoc pravidlo jak porovnávat boxované a neboxované věci? Nevyplývá to z nějakého obecnějšího pravidla pro řešení podobných neurčitých situací? Dávalo by mi to smysl i pro výběr přetížených metod, ale tam to evidentně funguje jinak. Čím dál tím lepší...
Je to totiž Havel!
Nic mi nedokážete, všechno popřu. 8)
-
Tuhle variantu už jste vy naimplementoval pro oba jazyky, a – světe div se – je to jak jsem psal, vychází to v obou jazycích stejně. Dovolte, abych z vašeho kódu ty dva řádky vypíchl:
No, nevychází, že ;D
System.out.println(a.equals(b)); // true
print (a == b) # True – používá se zde přetížený operátor ==
Přesně tak. Čitelnější to být nemůže.
Java, int: ekvivalence, použije se ==. Porovnání identity, zde popravdě netuším.
Java, objekt: ekvivalence, použije se operátor/metoda equals(). Porovnání identity, použije se operátor ==
Python, int: ekvivalence, použije se operátor ==. Porovnání identity, použije se operátor is.
Python, objekt: ekvivalence, použije se operátor ==, který se interně převede na __eq__. Porovnání identity, použije se operátor is.
A takhle, jak je to v Pythonu to mám rád.
Popravdě, zase si moc nefanděte. Já to píšu jen tak jako cvičení. Opravdu si nedělám naděje vám cokoliv vysvětlit ;D
-
K tématu:
Uvažoval jsem, že některé vlastnosti OOP se moc neosvědčili. Dědičnost a abstraktní třídy, vzor template method. Místo toho mixiny, nebo traity. Tedy znovupoužitelné fragmenty kódu, bez zanášení typu. Jestli ponechat dědičnost na úrovni rozhraní, nebo ani v tomto případě ne?
-
Takže je to speciální ad-hoc pravidlo jak porovnávat boxované a neboxované věci? Nevyplývá to z nějakého obecnějšího pravidla pro řešení podobných neurčitých situací? Dávalo by mi to smysl i pro výběr přetížených metod, ale tam to evidentně funguje jinak. Čím dál tím lepší...
Ve specifikaci je to speciální pravidlo, když ho budu parafrázovat „pokud jsou oba operandy číslo nebo je jeden operand číslo a druhý je převoditelný na číslo (unboxováním), nejprve se převoditelný typ převede na číslo“. U booleovských hodnot je to podobné.
U metod se vybírá co nejmenší počet boxování/unboxování, a pokud by z toho vyšlo víc metod, je to kompilační chyba (např. dvě metody se dvěma parametry, jedna s malými inty a druhá s velkými Integery a volalo by se to s jedním malým a druhým velkým).
Zkrátka se to chová tak, jak by člověk intuitivně očekával. A na tyhle nuance člověk narazí velmi výjimečně, a pokud by se to náhodou stalo, tak nebude spoléhat na automatiku a udělá ten un/boxing ručně. Teoreticky by z toho posloupností špatných událostí mohla vzniknout chyba, ale až tyhle chyby budou ty nejčastější chyby v softwaru, budeme mít bezchybný software.
-
Jestli ponechat dědičnost na úrovni rozhraní, nebo ani v tomto případě ne?
Co dobrého a netriviálního by podle tebe přineslo to mít? Já mám pocit, že dědičnost kdekoli dělá víc škody než užitku*.
Mně by se spíš líbilo to nahradit pěkným, stručným skládáním. Je dost škoda, když je v některých jazycích potřeba celé API vloženého objektu znovu zopakovat na tom vnějším (mám matný pocit, že to tak má někde Rust, ale přesně si to teď nevybavím).
----
* dědičnost má imho technické a filosofické problémy. Dědění tříd má oba typy problémů a relativně velký přínos, dědění interfejsů má spíš jenom filosofické problémy a přínos imho celkem malý. Takže bych se s tím nesral a vyhodil oboje ;)
-
No, nevychází, že ;D
Přesně tak. Čitelnější to být nemůže.
Jasně, v prvním případě hodnota pro pravdivý výrok, v druhém případě hodnota pro pravdivý výrok. V tom je takový rozdíl… Já už vážně nevím, co tam vidíte za rozdíl. Jako ano, jednou je tam true a po druhé True, jednou malé „t“ a podruhé velké „T“. Ale důvod, proč se liší velikost toho T snad chápete, ne?
Ano, přesně jak jsem předpovídal, následuje nářek, že jsem sice celou dobu psal o operátoru ==, ale to jsem určitě udělal naschvál, aby si BoneFlute myslel, že píšu o ekvivalenci nebo identitě. Zákeřně jsem tam čtyřikrát napsal slovo „operátor, dvakrát == a jednou __eq__, a ještě zákeřnější bylo, že jsem tam slovo „identita“ neuvedl ani jednou. Přeci mi muselo být nad slunce jasné, že si BoneFlute bude myslet, že celou dobu píšu o identitě. Teda ale fuj, že se nestydím.
Java, int: ekvivalence, použije se ==. Porovnání identity, zde popravdě netuším.
Java, objekt: ekvivalence, použije se operátor/metoda equals(). Porovnání identity, použije se operátor ==
Python, int: ekvivalence, použije se operátor ==. Porovnání identity, použije se operátor is.
Python, objekt: ekvivalence, použije se operátor ==, který se interně převede na __eq__. Porovnání identity, použije se operátor is.
Flamewar je sice divná zábava, ale zábava je to jenom dokud ho vedete s chytrými lidmi. Takže BoneFlute, mějte se dobře, naše konverzace tímto končí.
-
Tak tenhle příspěvek mě moc neuklidnil.
Takže je to speciální ad-hoc pravidlo jak porovnávat boxované a neboxované věci? Nevyplývá to z nějakého obecnějšího pravidla pro řešení podobných neurčitých situací? Dávalo by mi to smysl i pro výběr přetížených metod, ale tam to evidentně funguje jinak. Čím dál tím lepší...
Ve specifikaci je to speciální pravidlo, když ho budu parafrázovat „pokud jsou oba operandy číslo nebo je jeden operand číslo a druhý je převoditelný na číslo (unboxováním), nejprve se převoditelný typ převede na číslo“. U booleovských hodnot je to podobné.
U metod se vybírá co nejmenší počet boxování/unboxování, a pokud by z toho vyšlo víc metod, je to kompilační chyba (např. dvě metody se dvěma parametry, jedna s malými inty a druhá s velkými Integery a volalo by se to s jedním malým a druhým velkým).
Zkrátka se to chová tak, jak by člověk intuitivně očekával. A na tyhle nuance člověk narazí velmi výjimečně, a pokud by se to náhodou stalo, tak nebude spoléhat na automatiku a udělá ten un/boxing ručně. Teoreticky by z toho posloupností špatných událostí mohla vzniknout chyba, ale až tyhle chyby budou ty nejčastější chyby v softwaru, budeme mít bezchybný software.
Když jsem to četl, tak ve mně hrklo, protože co nejmenší počet boxování a unboxování mi přijde jako další neintuitivní past. Naštěstí to není pravda. Když jsem to zkoušel tak mě překladač seřval vždycky. Aspoň v něčem se teda Java chová tak, jak bych čekal.
-
Mně by se spíš líbilo to nahradit pěkným, stručným skládáním. Je dost škoda, když je v některých jazycích potřeba celé API vloženého objektu znovu zopakovat na tom vnějším (mám matný pocit, že to tak má někde Rust, ale přesně si to teď nevybavím).
Takže nakonec to přece jenom bude JavaScript? ;-) (Spread operátor na objektových literálech v ECMAScript 2018.)
-
Flamewar je sice divná zábava, ale zábava je to jenom dokud ho vedete s chytrými lidmi. Takže BoneFlute, mějte se dobře, naše konverzace tímto končí.
Jsem rád, že tu zůstanu sám s chytrýma.. ale klidně zas někdy přijď. ;D
-
Když jsem to četl, tak ve mně hrklo, protože co nejmenší počet boxování a unboxování mi přijde jako další neintuitivní past. Naštěstí to není pravda. Když jsem to zkoušel tak mě překladač seřval vždycky. Aspoň v něčem se teda Java chová tak, jak bych čekal.
Aha, ono se rozlišuje jenom žádné (un)boxování, nějaké (un)boxování. Vždyť jsem říkal, že se s tím v praxi nikdo nikdy nepotká.
-
Takže nakonec to přece jenom bude JavaScript? ;-) (Spread operátor na objektových literálech v ECMAScript 2018.)
JS není špatný v principu, ale v tom, že se tam doprasilo co mohlo. Lua je "JS done right", nezaznamenal jsem, že by s ní kdokoli měl problém. Ne že by to byl jazyk, ve kterém bych nějak zvlášť chtěl dělat, ale je to jazyk hodný respektu.
-
Jestli ponechat dědičnost na úrovni rozhraní, nebo ani v tomto případě ne?
Co dobrého a netriviálního by podle tebe přineslo to mít? Já mám pocit, že dědičnost kdekoli dělá víc škody než užitku*.
To úplně tak promyšlený nemám. Jednou jsem si hrál s něčím takovým:
Article
ArticleIdentification <- Article
ArticleSlugIdentification <- ArticleIdentification
ArticleIdIdentification <- ArticleIdentification
ArticleFullIdentification <- ArticleIdentification
ArticlePreview <- Article, ArticleIdentification
ArticleDetail <- Article, ArticleIdentification
ArticleModify <- Article, ArticleIdentification, Command
ArticleCreate <- Article, Command
Smysl to dávalo, neměl jsem možnost to pořádně prozkoumat praxí. A dráždí mě tam ta evidentní ukecanost.
Mně by se spíš líbilo to nahradit pěkným, stručným skládáním. Je dost škoda, když je v některých jazycích potřeba celé API vloženého objektu znovu zopakovat na tom vnějším (mám matný pocit, že to tak má někde Rust, ale přesně si to teď nevybavím).
Ano. Psal jsem o traitech a mixinech. To považuji za vhodného kadidáta.
-
Takže nakonec to přece jenom bude JavaScript? ;-) (Spread operátor na objektových literálech v ECMAScript 2018.)
JS není špatný v principu, ale v tom, že se tam doprasilo co mohlo. Lua je "JS done right", nezaznamenal jsem, že by s ní kdokoli měl problém. Ne že by to byl jazyk, ve kterém bych nějak zvlášť chtěl dělat, ale je to jazyk hodný respektu.
+1
-
K tématu:
Uvažoval jsem, že některé vlastnosti OOP se moc neosvědčili. Dědičnost a abstraktní třídy, vzor template method. Místo toho mixiny, nebo traity. Tedy znovupoužitelné fragmenty kódu, bez zanášení typu. Jestli ponechat dědičnost na úrovni rozhraní, nebo ani v tomto případě ne?
Jop, type classy z Haskellu a traity z Rustu jsou asi nejlepší přístup k polymorfismu, co jsem zatím potkal. Koncepty v novém C++ se tím hodně inspirovaly, takže vypadají taky až překvapivě dobře.
Dědičnost bych nezavrhoval úplně, ale souhlasím že pro intuitivní implementaci vztahu "is-a" se moc nehodí. I když se tak často prezentuje.
-
Dědičnost bych nezavrhoval úplně, ale souhlasím že pro intuitivní implementaci vztahu "is-a" se moc nehodí. I když se tak často prezentuje.
Na vztahy jsem uvažoval v duchu Prologu. Ale zatím jsem nic nevymyslel :-)
-
Takže nakonec to přece jenom bude JavaScript? ;-) (Spread operátor na objektových literálech v ECMAScript 2018.)
JS není špatný v principu, ale v tom, že se tam doprasilo co mohlo. Lua je "JS done right", nezaznamenal jsem, že by s ní kdokoli měl problém. Ne že by to byl jazyk, ve kterém bych nějak zvlášť chtěl dělat, ale je to jazyk hodný respektu.
S Luou občas dělám. Ta kombinace minimalismu a síly je opravdu výjimečná. Dost lidí se na ní AFAIK nelíbí indexování polí od jedničky, ale to je podle mě detail.
Co mi na ní opravdu vadí je dynamické kachní typování. I hodně stupidní překlepy odhalím až k nim program dojde. A ne všechno se dá otestovat pomocí malých a rychlých testů. Takže to občas vypadá tak, že dlouho čekám, pak opravím nějakou blbost a to celé několikrát dokola.
-
Smysl to dávalo, neměl jsem možnost to pořádně prozkoumat praxí. A dráždí mě tam ta evidentní ukecanost.
Podle me tech domen, ktere skutecne jdou modelovat hierarchii bez jakychkoli cyklu (treba i "weak linku"), je naprosty minimum. Zavadet kvuli nim vec, ktera jazyk netrivialne zesloziti (kovariance, kontravariance, ...) proste a jednoduse nestoji za to ;)
Ano. Psal jsem o traitech a mixinech. To považuji za vhodného kadidáta.
Jj. Ale dabel je v detailu. O Go jsem si myslel, ze jde presne spravnou cestou, dokud jsem ho nezacal pouzivat a nenapsal v nem neco aspon trochu abstraktniho, coz bylo dost utrpeni :)
Na vztahy jsem uvažoval v duchu Prologu. Ale zatím jsem nic nevymyslel :-)
Hlavne s tim nic nevymyslej, abys promatkuprirodu nahodou nevymyslel neco, co byse mohlo chytnout! ;)
Prolog je uzasnej jako "inteligentni databaze". Ale programovat v tom "imperativne" je jak zvykat si jazyk do krve a strilet se pri tom do nohy, to cely za zvuku TV Slagr.
Docela se mi na jednom pokusu osvedcila kombinace normalni jazyk na logiku a "control" + embeddovany prolog na rozhodovani. To bylo celkem poteseni s tim delat, protoze obe casti delaly, co umi nejlip, a ani jednu jsem neznasilnoval k necemu, do ceho se ji moc nechce...
-
Co mi na ní opravdu vadí je dynamické kachní typování. I hodně stupidní překlepy odhalím až k nim program dojde. A ne všechno se dá otestovat pomocí malých a rychlých testů. Takže to občas vypadá tak, že dlouho čekám, pak opravím nějakou blbost a to celé několikrát dokola.
Njn, to je dan za dynamicnost. Proto rikam, ze bych s ni (neco vylozene vaznyho) moc delat nechtel.
-
Nic mi nedokážete, všechno popřu. 8)
:)
-
Ano. Psal jsem o traitech a mixinech. To považuji za vhodného kadidáta.
Jj. Ale dabel je v detailu. O Go jsem si myslel, ze jde presne spravnou cestou, dokud jsem ho nezacal pouzivat a nenapsal v nem neco aspon trochu abstraktniho, coz bylo dost utrpeni :)
Něco konkrétnějšího by nebylo?
Na vztahy jsem uvažoval v duchu Prologu. Ale zatím jsem nic nevymyslel :-)
Hlavne s tim nic nevymyslej, abys promatkuprirodu nahodou nevymyslel neco, co byse mohlo chytnout! ;)
Slibuju. Až to vymyslím, tobě to natruc neřeknu.
Prolog je uzasnej jako "inteligentni databaze". Ale programovat v tom "imperativne"
Protože od toho Prolog není. Právě proto píšu, že na vztahy.
-
Něco konkrétnějšího by nebylo?
Uz jsem tady o tom nekdy psal, nechci se opakovat. A hlavne to byly takovy veci, ktery se tezko vysvetluji, aniz bych popsal celej problem a pulku zdrojaku :) Proste jsem z toho jazyka byl zklamanej, no. A nejkonkretnejsi uroven, na ktere se mi to chce rozebirat je asi tahle: prijde mi, ze ten jazyk ma strasne nizkou schopnost vyjadrovat abstraktni myslenky. Nejvic asi kvuli chybejicim generikum, ale nejenom. Ja tam nekolik takovych veci, ktery samy o sobe vypadaji jako drobnost (napr. https://forum.root.cz/index.php?topic=21149.msg310682#msg310682 ), ale jako celek mi to proste pro cokoli abstraktniho nevoni. Zas ale na jednoduche, predem zname a dobre analyzovane problemy, je to docela dobrej jazyk (z tech dost mainstreamovych, aby je davalo smysl pouzivat).
Takove celkem slusne nizkoprahove centrum pro deti a mladez ;)
Slibuju. Až to vymyslím, tobě to natruc neřeknu.
Fair enough! ;)
Protože od toho Prolog není. Právě proto píšu, že na vztahy.
Jj, ale to uz pak nemas jazyk jako takovy, spis nejaky jeho doplnek - modul, databazi, subsystem... Jo, v takove roli mi Prolog dava smysl skvele.
-
K tématu:
Uvažoval jsem, že některé vlastnosti OOP se moc neosvědčili. Dědičnost a abstraktní třídy, vzor template method. Místo toho mixiny, nebo traity. Tedy znovupoužitelné fragmenty kódu, bez zanášení typu. Jestli ponechat dědičnost na úrovni rozhraní, nebo ani v tomto případě ne?
Jop, type classy z Haskellu a traity z Rustu jsou asi nejlepší přístup k polymorfismu, co jsem zatím potkal. Koncepty v novém C++ se tím hodně inspirovaly, takže vypadají taky až překvapivě dobře.
Dědičnost bych nezavrhoval úplně, ale souhlasím že pro intuitivní implementaci vztahu "is-a" se moc nehodí. I když se tak často prezentuje.
Typové třídy bohužel vyžadují higher kinded types, kteréžto má z mainstreamu (částečně) jen C++.
-
Jestli ponechat dědičnost na úrovni rozhraní, nebo ani v tomto případě ne?
Co dobrého a netriviálního by podle tebe přineslo to mít? Já mám pocit, že dědičnost kdekoli dělá víc škody než užitku*.
To úplně tak promyšlený nemám. Jednou jsem si hrál s něčím takovým:
Article
ArticleIdentification <- Article
ArticleSlugIdentification <- ArticleIdentification
ArticleIdIdentification <- ArticleIdentification
ArticleFullIdentification <- ArticleIdentification
ArticlePreview <- Article, ArticleIdentification
ArticleDetail <- Article, ArticleIdentification
ArticleModify <- Article, ArticleIdentification, Command
ArticleCreate <- Article, Command
Smysl to dávalo, neměl jsem možnost to pořádně prozkoumat praxí. A dráždí mě tam ta evidentní ukecanost.
Smysl to nedává, protože už u "ArticleIdentification <- Article" je porušeno zmíněné pravidlo "is-a". Následující identifikátory na tom nejsou lépe.
-
Něco konkrétnějšího by nebylo?
Uz jsem tady o tom nekdy psal, nechci se opakovat. A hlavne to byly takovy veci, ktery se tezko vysvetluji, aniz bych popsal celej problem a pulku zdrojaku :) Proste jsem z toho jazyka byl zklamanej, no. A nejkonkretnejsi uroven, na ktere se mi to chce rozebirat je asi tahle: prijde mi, ze ten jazyk ma strasne nizkou schopnost vyjadrovat abstraktni myslenky. Nejvic asi kvuli chybejicim generikum, ale nejenom. Ja tam nekolik takovych veci, ktery samy o sobe vypadaji jako drobnost (napr. https://forum.root.cz/index.php?topic=21149.msg310682#msg310682 ), ale jako celek mi to proste pro cokoli abstraktniho nevoni. Zas ale na jednoduche, predem zname a dobre analyzovane problemy, je to docela dobrej jazyk (z tech dost mainstreamovych, aby je davalo smysl pouzivat).
existuji alternativy
https://crystal-lang.org/
-
Typové třídy bohužel vyžadují higher kinded types, kteréžto má z mainstreamu (částečně) jen C++.
Ono by (aspoň teoreticky) šlo mít i typové třídy jenom prvního řádu, ne? Nešlo by pak definovat takové třídy jako Functor, ale šlo by udělat Vector[T], kde T je proper type. To by možná mohlo z praktického/pragmatického hlediska celkem i stačit, ne? Java to tak má/měla, ne?
-
existuji alternativy
https://crystal-lang.org/
Jo, jazyků je bambilion. V tom problém není. Problém je tradeoff mezi rozumností jazyka a dostatečným rozšířením a supportem.
-
existuji alternativy
https://crystal-lang.org/
Jo, jazyků je bambilion. V tom problém není. Problém je tradeoff mezi rozumností jazyka a dostatečným rozšířením a supportem.
https://github.com/veelenga/awesome-crystal
-
https://github.com/veelenga/awesome-crystal
Jde spis o lidi nez o knihovny...
-
Typové třídy bohužel vyžadují higher kinded types, kteréžto má z mainstreamu (částečně) jen C++.
Ono by (aspoň teoreticky) šlo mít i typové třídy jenom prvního řádu, ne? Nešlo by pak definovat takové třídy jako Functor, ale šlo by udělat Vector[T], kde T je proper type. To by možná mohlo z praktického/pragmatického hlediska celkem i stačit, ne? Java to tak má/měla, ne?
To pak ale nic moc neřeší. Bohatě by stačilo, kdyby typové třídy nad HKT používala standarní knihovna (třeba Swift k tomu směřuje), běžný Jouda pak jen použije typ z knihovny, aniž by musel rozumět sofistikované vnitřní implementaci.
P.S. (mírně OT) V souvislosti s tímto se mi do ruky nedávno dostal nový výukový text o monádách a myslím, že trefili funktor na hlavičku.
-
To pak ale nic moc neřeší.
Ale jo. Resi to tu nejvetsi (z myho pohledu) bolest Go: mam neco, co chci implementovat pro ruzne typy, a nemusim mit jak dement v interfejsu bambilion uplne stejnych funkci pro jednotlive typy (treba tohle https://godoc.org/go.uber.org/zap#Field je proste k zbliti, jinak se to neda nazvat). Jenom tohle mi prijde, ze pokryva 90% "abstraktniho programovani" ve skutecne praxi.
Bohatě by stačilo, kdyby typové třídy nad HKT používala standarní knihovna (třeba Swift k tomu směřuje), běžný Jouda pak jen použije typ z knihovny, aniž by musel rozumět sofistikované vnitřní implementaci.
Jo, to by asi nebylo uplne blby. Gocko ma uzivatelske typy jenom nulteho radu a v stdlib ma par prvniho radu. Tohle posunout o uroven vys (uzivatelske prvniho radu a v stdlib HKT) by mohlo byt zajimavy. Pokud by to teda slo vymyslet tak, aby to fakt bezny franta programator pobral (tj. aby to nezkomplikovalo typovy system natolik, ze by se v nem nevyznal).
P.S. (mírně OT) V souvislosti s tímto se mi do ruky nedávno dostal nový výukový text o monádách a myslím, že trefili funktor na hlavičku.
Horim zvedavosti :) Mas odkaz? (pripadne muzes poslat PM?)
-
https://github.com/veelenga/awesome-crystal
Jde spis o lidi nez o knihovny...
komunita uz je velka a roste. Prechazi na to i Elixiristi.
-
Prechazi na to i Elixiristi.
Mozna ti, co prisli z Ruby ;)
-
To pak ale nic moc neřeší.
Ale jo. Resi to tu nejvetsi (z myho pohledu) bolest Go: mam neco, co chci implementovat pro ruzne typy, a nemusim mit jak dement v interfejsu bambilion uplne stejnych funkci pro jednotlive typy (treba tohle https://godoc.org/go.uber.org/zap#Field je proste k zbliti, jinak se to neda nazvat). Jenom tohle mi prijde, ze pokryva 90% "abstraktniho programovani" ve skutecne praxi.
Bohatě by stačilo, kdyby typové třídy nad HKT používala standarní knihovna (třeba Swift k tomu směřuje), běžný Jouda pak jen použije typ z knihovny, aniž by musel rozumět sofistikované vnitřní implementaci.
Jo, to by asi nebylo uplne blby. Gocko ma uzivatelske typy jenom nulteho radu a v stdlib ma par prvniho radu. Tohle posunout o uroven vys (uzivatelske prvniho radu a v stdlib HKT) by mohlo byt zajimavy. Pokud by to teda slo vymyslet tak, aby to fakt bezny franta programator pobral (tj. aby to nezkomplikovalo typovy system natolik, ze by se v nem nevyznal).
P.S. (mírně OT) V souvislosti s tímto se mi do ruky nedávno dostal nový výukový text o monádách a myslím, že trefili funktor na hlavičku.
Horim zvedavosti :) Mas odkaz? (pripadne muzes poslat PM?)
Odkaz nemám, jen jsem recenzoval materiály pro jednu anglickou univerzitu. Ale úvod začíná takto: “The notion of a monad is best introduced through particular examples. A formal definition exists but is practically useless until some intuition has been developed. It is not given in this course.” To mě zaujalo.
-
Odkaz nemám, jen jsem recenzoval materiály pro jednu anglickou univerzitu. Ale úvod začíná takto: “The notion of a monad is best introduced through particular examples. A formal definition exists but is practically useless until some intuition has been developed. It is not given in this course.” To mě zaujalo.
To zni dobre :)) Ale divim se, ze zrovna ty na to spis nenadavas :)
-
Odkaz nemám, jen jsem recenzoval materiály pro jednu anglickou univerzitu. Ale úvod začíná takto: “The notion of a monad is best introduced through particular examples. A formal definition exists but is practically useless until some intuition has been developed. It is not given in this course.” To mě zaujalo.
To zni dobre :)) Ale divim se, ze zrovna ty na to spis nenadavas :)
Proč? Mám poměrně čerstvé zkušenosti s jejich materiály o fyzice a matematickém modelování (kvantová mechanika, nelineární chaos :) ) a musím nadšeně přiznat, že tým prof. Nortona napsal nejlepší učebnici, co jsem kdy viděl. They cracked it. Nevím, kdo psal tu učebnici o funkcionálním programování, ale je stejně kvalitní. Budu se opakovat, ale fakt mě nadchla.
-
Nevím, kdo psal tu učebnici o funkcionálním programování, ale je stejně kvalitní. Budu se opakovat, ale fakt mě nadchla.
Vis neco o tom, kdy a kde bude k dispozici?
-
Nevím, kdo psal tu učebnici o funkcionálním programování, ale je stejně kvalitní. Budu se opakovat, ale fakt mě nadchla.
Vis neco o tom, kdy a kde bude k dispozici?
Oni to většinou dávají jen ke svým kurzům, nevím o prodávání zvlášť, ale na eBayi stačí zadat “Open University”. Někdy na podzim to asi začnou rozesílat studentům, takže po semestru se to objeví na webu k prodeji.
-
na eBayi stačí zadat “Open University”.
Na ebayi mi nabizi jenom University Of Delaware 7” Heavy Duty Bottle Opener :))
-
na eBayi stačí zadat “Open University”.
Na ebayi mi nabizi jenom University Of Delaware 7” Heavy Duty Bottle Opener :))
eBay.co.uk?
-
eBay.co.uk?
Na tom knizky jsou. Jak se ten kurz, pro kterej je tenhle material bude jmenovat? Co hledat?
-
eBay.co.uk?
Na tom knizky jsou. Jak se ten kurz, pro kterej je tenhle material bude jmenovat? Co hledat?
Algorithms, data structures and computability ;)
-
eBay.co.uk?
Na tom knizky jsou. Jak se ten kurz, pro kterej je tenhle material bude jmenovat? Co hledat?
http://www.open.ac.uk/courses/qualifications/details/m269?orig=q62
-
Super, dik!
-
http://www.open.ac.uk/courses/qualifications/details/m269?orig=q62
Ten link nefunguje, ale to je jedno, to uz si dohledam.
-
http://www.open.ac.uk/courses/qualifications/details/m269?orig=q62
Ten link nefunguje, ale to je jedno, to uz si dohledam.
Tak openuniversity.edu a tam M269
-
To pak ale nic moc neřeší.
Ale jo. Resi to tu nejvetsi (z myho pohledu) bolest Go: mam neco, co chci implementovat pro ruzne typy, a nemusim mit jak dement v interfejsu bambilion uplne stejnych funkci pro jednotlive typy (treba tohle https://godoc.org/go.uber.org/zap#Field je proste k zbliti, jinak se to neda nazvat). Jenom tohle mi prijde, ze pokryva 90% "abstraktniho programovani" ve skutecne praxi.
Bohatě by stačilo, kdyby typové třídy nad HKT používala standarní knihovna (třeba Swift k tomu směřuje), běžný Jouda pak jen použije typ z knihovny, aniž by musel rozumět sofistikované vnitřní implementaci.
Jo, to by asi nebylo uplne blby. Gocko ma uzivatelske typy jenom nulteho radu a v stdlib ma par prvniho radu. Tohle posunout o uroven vys (uzivatelske prvniho radu a v stdlib HKT) by mohlo byt zajimavy. Pokud by to teda slo vymyslet tak, aby to fakt bezny franta programator pobral (tj. aby to nezkomplikovalo typovy system natolik, ze by se v nem nevyznal).
Běžný Franta vůbec nemá co psát typy, pro něj je typová inference. S těmi knihovnami to je také jednoduché, někdo na úrovni prostě napíše supersofistikovaný kód (hezký příklad: http://ericniebler.com/2013/07/16/f-algebras-and-c/) a běžný Franta ho jen přímo používá, aniž by si nad ním musel lámat hlavu. Takto jdou napsat třeba obecné monády v C++ (stejně jako v Haskellu) a pak jen implementovat konkrétní třídy, třeba seznam nebo kontinuaci, přičemž join dostanu zadarmo z bind, nebo fmap apod. Bohužel tohle mainstream moc neumí, ani Java, ani C#, ani Swift. A v tom C++ to sice jde, ale hnusně.
U toho Go - teď trochu odbočuju - vidím jednu velkou výhodu v jednotné volací konvenci. Z praxe - píšu knihovnu pro HPC využívající AVX-512. Normálně bych musel mít i pro amd64 pro různé OS různé verze funkcí v asembleru kvůli odlišným volacím konvencím. V Go rozhoduje jen CPU, pro amd64 napíšu jeden asembler pro všechny OS.
-
Běžný Franta vůbec nemá co psát typy, pro něj je typová inference.
Vetšinou ne, ale občas mi přijde explicitně napsaný typ jako skvělý způsob dokumentace, komentáře a zpřehlednění kódu obecně.
S těmi knihovnami to je také jednoduché, někdo na úrovni prostě napíše supersofistikovaný kód (hezký příklad: http://ericniebler.com/2013/07/16/f-algebras-and-c/) a běžný Franta ho jen přímo používá, aniž by si nad ním musel lámat hlavu.
V ideálním případě by to tak šlo. Ale občas ten Franta narazí na případy kdy se musí skrz ten supersofistikovaný kód prokrokovat. Nebo třeba udělá nějakou obskurnější chybu a bude hledat, co se vůbec stalo. Pokud ten supersofistikovaný kód může vidět, tak musí být schopný mu i _přiměřeně_ rozumět. To, že by to nebyl schopný napsat, je druhá věc.
Takto jdou napsat třeba obecné monády v C++ (stejně jako v Haskellu) a pak jen implementovat konkrétní třídy, třeba seznam nebo kontinuaci, přičemž join dostanu zadarmo z bind, nebo fmap apod. Bohužel tohle mainstream moc neumí, ani Java, ani C#, ani Swift. A v tom C++ to sice jde, ale hnusně.
Zrovna u monád je to "hnusně" docela vážný zádrhel. Monády mi přijdou jako vzor, který je sice všudypřítomný ale vlastně dělá malé věci. Takový druh vzoru jako byla třeba subrutina, než se to usadilo tak, že už o tom ani neuvažujeme jako o vzoru. Takže ty hnusné kousky kódu se pak vyskytují všude. Není to kýbl hnusu, který by se dal zabalit do nějakého modulu, jako jsou třeba windows.h.
U toho Go - teď trochu odbočuju - vidím jednu velkou výhodu v jednotné volací konvenci. Z praxe - píšu knihovnu pro HPC využívající AVX-512. Normálně bych musel mít i pro amd64 pro různé OS různé verze funkcí v asembleru kvůli odlišným volacím konvencím. V Go rozhoduje jen CPU, pro amd64 napíšu jeden asembler pro všechny OS.
Tak tohle mě zaujalo. Proč je to třeba psát v asm? Co brání použití intrinsik?
-
Běžný Franta vůbec nemá co psát typy, pro něj je typová inference.
Vetšinou ne, ale občas mi přijde explicitně napsaný typ jako skvělý způsob dokumentace, komentáře a zpřehlednění kódu obecně.
S těmi knihovnami to je také jednoduché, někdo na úrovni prostě napíše supersofistikovaný kód (hezký příklad: http://ericniebler.com/2013/07/16/f-algebras-and-c/) a běžný Franta ho jen přímo používá, aniž by si nad ním musel lámat hlavu.
V ideálním případě by to tak šlo. Ale občas ten Franta narazí na případy kdy se musí skrz ten supersofistikovaný kód prokrokovat. Nebo třeba udělá nějakou obskurnější chybu a bude hledat, co se vůbec stalo. Pokud ten supersofistikovaný kód může vidět, tak musí být schopný mu i _přiměřeně_ rozumět. To, že by to nebyl schopný napsat, je druhá věc.
Takto jdou napsat třeba obecné monády v C++ (stejně jako v Haskellu) a pak jen implementovat konkrétní třídy, třeba seznam nebo kontinuaci, přičemž join dostanu zadarmo z bind, nebo fmap apod. Bohužel tohle mainstream moc neumí, ani Java, ani C#, ani Swift. A v tom C++ to sice jde, ale hnusně.
Zrovna u monád je to "hnusně" docela vážný zádrhel. Monády mi přijdou jako vzor, který je sice všudypřítomný ale vlastně dělá malé věci. Takový druh vzoru jako byla třeba subrutina, než se to usadilo tak, že už o tom ani neuvažujeme jako o vzoru. Takže ty hnusné kousky kódu se pak vyskytují všude. Není to kýbl hnusu, který by se dal zabalit do nějakého modulu, jako jsou třeba windows.h.
U toho Go - teď trochu odbočuju - vidím jednu velkou výhodu v jednotné volací konvenci. Z praxe - píšu knihovnu pro HPC využívající AVX-512. Normálně bych musel mít i pro amd64 pro různé OS různé verze funkcí v asembleru kvůli odlišným volacím konvencím. V Go rozhoduje jen CPU, pro amd64 napíšu jeden asembler pro všechny OS.
Tak tohle mě zaujalo. Proč je to třeba psát v asm? Co brání použití intrinsik?
Ano, máte pravdu, já jsem to napsal zkratkovitě, samozřejmě je lepší, když uživatel knihovny aspoň zběžně tuší, jak to funguje pod kapotou. Ta hranice není ostrá, je dobře, že to berete s nadhledem.
K tomu SIMD: Intrinsics mají určitou režii (překladač si pořád řídí proměnné, cykly apod.), v asembleru mám absolutní kontrolu. Navíc v tom Go žádné intrinsics nejsou.