Co se mi nelíbí na Go
Školení, která pořádám
Go je relativně nový programovací jazyk navržený v Google. Setkal jsem se s názorem, že do pěti let půjde o nejpoužívanější programovací jazyk, tak jsem se přihlásil na školení základů tohoto jazyka. Jinou zkušenost s Go nemám, takže moje postřehy jsou povrchní a nepodložené praxí (kromě příkladů, které jsme na školení dělali). Některé body se v praxi jako tak problematické projevit nemusí, některé další naopak můžou vyplout na povrch.
Go považuji za celkem zdařilý programovací jazyk, ovlivněný z mého pohledu především JavaScriptem, Pythonem a C. Místy dosahuje expresivity Perlu, ale přitom zůstává čitelný. Co se mi nelíbí?
- Ukazatele. V C a C++ jsem ukazatele vždycky nesnášel. V Go jsou naštěstí mnohem umírněnější, např. nejde ukazatel přetypovat, stejně by se bez nich ale podle mě vysokoúrovňový programovací jazyk měl obejít. PHP má místo ukazatelů reference, ty bych klidně taky zrušil (např. Recki-CT je nepodporuje).
- Magie. Dereferenci pointeru někde udělá kompilátor za vás, někde ji musíte udělat sami. Nezapamatoval jsem si, kde přesně, a nevidím důvod, proč by to nemohlo jít skoro všude. Typy na některých místech uvádět musíte, jinde ne. To je ale naštěstí celkem logické. V definici konstant se za určitých okolností nemusí uvádět hodnota a odvodí se z předchozí konstanty.
- Ošetření chyb. Podle mě jde o nejzásadnější problém jazyka. Neexistují výjimky, místo toho funkce obvykle vrací dvojici návratových hodnot (výsledek, chyba). Volající musí chybovou část návratové hodnoty zkontrolovat. Pokud to neudělá, tak se nic nestane. Pokud k chybě dojde, funkce stejně musí něco předat i ve výsledku, např. (0, chyba), což jen podporuje volajícího v tom, aby chybu nekontroloval. Pokud chcete zjistit, jestli v mapě existuje nějaký prvek, tak se to dělá také druhou návratovou hodnotou, tentokrát ale v přesně opačném významu než u funkcí (
v, ok := a[1]
oproti v, err = f()
).
- Nejednotnost. Funkce
append
změněný slice vrátí, funkce delete
mapu změní na místě.
- Zkratky. Na většině míst to ničemu nevadí a řádky jsou aspoň kratší. Ale aby se např. zřídka používaná funkce pro zjištění kapacity musela jmenovat
cap
, to si nemyslím.
- Slices. Slice je jen pohled na pole. Když máte dva různé pohledy na to stejné pole a změníte hodnotu v jednom z nich, tak se změní i ten druhý. Klidně můžete přistupovat i k prvkům za délkou slice, pokud to kapacita jeho pole umožňuje. Nevím, jestli tyto vlastnosti vedou k reálným chybám, ale přijdou mi velmi nebezpečné.
Go má i řadu příjemných vlastností, ale tyto problémy jsou pro mě natolik zásadní, že se Go nechystám začít používat.
Diskuse
Neexistence výjimek jako problém nevidím. Lze to nahradit vhodnout návratovou hodnotou a rozlišovat pomocí typu. Either, Maybe, například. Na tento přístup jsem si velice rád zvykl, a považuji jej dokonce za lepší, než výjimky.
Ale řešení, které zvolili u Go, pokud je to pravda jak to popisuješ, vypadá jako dobrej masakr.
To záleží. U funkcionálního přístupu je Maybe, Try apod. určitě lepší. Díky tomu se těžko stane, ze něco neošetříš.
Naopak u imperativního přístupu je to pohroma. Něco zapomenu ošetřit a vše se snaží tvářit OK. Průšvih je to třeba u setuid: http://thesnkchrmr.wordpress.com/2011/03/24/rageagainstthecage/
Výjimky sice nedokopou programátora k ošetření chyby (s výjimkou checked exceptions), ale aspoň program spadne a vyhlásí chybu.
Za rule od thumb považuju: Má ta funkce sice effect? Alternativně: Jak je pravděpodobné, že programátora návratová hodnota nebude zajímat?
Taco:
Tak tu souvislost s FP jsem si neuvědomil. Dík za postřeh.
KarelL:
Výjimky neexistují ani v C.
A co jste na ukazatelích v C a C++ nesnášel?
Ukazatele považuji za zbytečnost. PHP, Java ani řada dalších programovacích jazyků je nemá a fungují dobře.
Když se podívám na nějaký zdroják v C, např. PHP, a vidím přetypování ukazatelů, tak to považuji za smetí, u kterého doufám, že dělá, co má. Ale představa, že tohle smetí do programu sám píšu, mě děsí.
Lukyer:
Možná by stačilo obětovat trochu času k jejich pochopení :-) S nepřehledností ukazatelů v x-dimenzionálních polích nicméně souhlasím.
Velká chyba, že jsi nezmínil alespoň nějaké příjemné vlastnosti, např. podpora paralelního programování. Celý článek je pak spíš jako hejt.
S těmi výjimkami to je přesně argument, co uvádí spousta lidí a já se k nim také přidávám. Tohle je návrat k prehistorii, ale spousta lidí to holt vnímá jinak. Další nezmíněná věc, co dost často chybí jsou např. generics.
Go je hlavně o tom, jak to vidí jeho autoři, o zásadních aspektech jazyka se nedá moc diskutovat.
Go se teď veze na dost velkém hype, počkal bych tak za dva tři roky, jak se to vyvine.
Martin Hruška:
Proč hejt? Vždyť je to jen čistě subjektivní "co se mi nelíbí".
Za svou praxi jsem zjistil, že lidem fungují mozky každému jinak a co sedí jednomu, nesedí druhému.
Jazyky, frameworky, konvence... Není nad to, zažít ten moment, kdy si způsob myšlení a jazyk skvěle sednou. Stačí nejít se stádem a sám zkoušet. Jakub zkouší a udělá si názor, se kterým můžeme nesouhlasit, ale nemůžeme mu ho vyvrátit. To je jak zkoušet někomu vymluvit homosexualitu. Každému se líbí něco.
Ad „Celý článek je pak spíš jako hejt.“
To je na tomhle webu standard, ne? :-)
Ale jinak celkem souhlas, jazyk bez výjimek a generik mě taky neláká. U moderního jazyka bych čekal, že tyhle vlastnosti nejen budou, ale že budou i nějak vylepšené oproti současným jazykům.
Mě se Go na první pohled líbilo, ale pak jsem viděl způsob řešení OOP (nemá třídy, jen struktury) a byl šmitec :)
Jako náhrada C je to asi dobré, ale jako univerzální jazyk mi to moc dobré nepřijde.
Michal Gebauer:
Struct je prakticky třída, navíc má kompozici. Děděné třídy jsou archaické zlo a go je řeší právě kompozicí a implicitními rozhraními.
Michal Gebauer:
ad 2) kde je magie? Zatím sem na ní nenarazil. Garance správného typu při přetypování je nejlepší co sem zatím viděl.
Michal Gebauer:
ad 4) to není nejednotnost append vrátí novou větší strukturu když je třeba zvětšit alokovanou paměť a proto je třeba vždy přiřadit x := append(x, y). (pointer může změnit hodnotu) U delete žádná další alokace není a proto není třeba nic vracet. AFAIK
Michal Gebauer:
ad 6) nelze přistupovat k s[N > len(s)] viz.
http://play.golang.org/p/-gVpxStZYZ kapacita slouží jen pro optimalizaci alokace paměti která většinou může zásadně ovlivňovat paměťovou náročnost a rychlost algoritmu při přidávání dalších prvků. Nejde ovlivňovat po vytvoření (stará se o to go: if len(s) == cap(s) y := alloc(cap(s)*2) copy(x, y); přesný kód je v dokumentaci)
Michal Gebauer:
Díky, to sem si neuvědomil. I append na slice se chová trochu divně, začne přepisovat původní pole když slice není na konci originálního pole.
Michal Gebauer:
ad 3) Chyby a pole spolu nesouvisí pole mají výchozí hodnoty (např.: nil) a při správném návrhu není třeba kontrolovat ok. Spíš zásadně zjednodušují kód a zmenšují chybovost oproti PHP.
Ale vnořené pole vo go jsou trochu bolest. PHP u polí má magie víc než dost.
Mě osobně result, err := fn(); nutí kontrolovat chyby a reágovat na ně. Bonusem je vzor pro chyby a jejich zpracování. Chyby knihovny exportují jako globální proměnné a lze je rychle a přehledně porovnávat bez nutnosti je vytvářet dokola.
if x, err := ns.fn(); err == ns.ErrorNotOk {
// handle ...
}
Bold:
Tyjo co je to za trend? Me osobne teda absence vyjimek taky vadi. Napriklad v Node.js sice pouzivat jdou, ale v urcitych fazich zivotniho cyklu jejich vyhozeni nemuze probublat do zadneho custom kodu a shodi server.
Porad testovat navratovou hodnotu mi prijde jako krok o 30 let zpatky.
Kámoš Hloupý:
Co na něj říkáte v roce 2018?
Jakub Vrána :
Stále žádná praktická zkušenost. Bavím se nad dlouholetým odmítáním generik a následnou snahou je přeci jen přidat.
Diskuse je zrušena z důvodu spamu.