Vélemények: Go, aka "Golang" programozási nyelv

Érdekelne a véleményetek, hogy ki mit gondol a Go programozási nyelvről, ki használta már, és hol. Vajon leváltja-e a Node.js, C++, Java, Ruby on Rails, Python-t, ha back-end fejlesztésre gondolunk. Köszönöm a válaszokat.

Hozzászólások

Szerintem nem. Egyszerűen nem sikerült elég momentumot szerezni. Érdemes megnézni, hogy mondjuk java-hoz, vagy node.js-hez milyen mennyiségben vannak könyvtárak. A magam részéről nem látom, hogy mi olyat tudna nyújtani, amit miatt átállna valaki java-ról, vagy mondjuk C#-ról go-ra.

Hát ha nem is a lib-ek miatt, akkor ottvan még jó pár dolog: goroutines, latency, compiled(pár másodperc alatt lefordulnak nagyobb project-ek is, egy darab file), memóriahasználat, olvashatóság, gyorsaság(50%-al lassabb csak, mint a C), és az a jó pár száz cikk, hogy nagyobb cégek, miért váltanak Go-ra.
Például:
http://marcio.io/2015/07/handling-1-million-requests-per-minute-with-go…
https://twitter.com/jbuberel/status/617776229437980673?s=09
http://www.shift8creative.com/posts/the-business-benefits-of-go/
https://developers.soundcloud.com/blog/go-at-soundcloud

Ha minden alkalommal tabula rasát csinált volna a biznisz, amikor megjelent egy új nyelv, valami frappáns fícsörrel, kb. a tetriszt reimplementálnánk százhuszadszorra (olykor hasonló be is indul, aztán jön a kontroller, vagy a soron következő válság, és kipofozza az örök álmodozók szeméből a csipát).

Ehelyett elfér a pályán a Fortran és a Go is, meg közöttük minden, ami szem-szájnak és vastagbélnek ingere.

Ugyanezt a történetet eljátszották már a C#, Java, Ruby, Python, Node.JS, stb.-nél is.

Illetve a fenti példánál maradva lényegében egy architekturális változtatást csináltak. Ez pont olyan, mint amikor a Node.JS bejött az egy szálú, callback-ekre
építő architekurával, és wow, az mennyire jó volt. Aztán a többi nyelvbe is bejött ez az architekturális megoldás. Ugyanezt eljátszotta a Ruby is korábban a Ruby On Rails-el,
aztán jött a symfony, play framework, meg az összes többi, hasonló tudású framework, és innentől megint mindenki maradhatott a saját, jól bevált programozási nyelvénél.

"és az a jó pár száz cikk, hogy nagyobb cégek, miért váltanak Go-ra.
Például: "

Mindig nagy a hype hasonloan uj nyelvek korul, pl. Scala-ra is "valtott" egy par "nagyobb ceg" es nyomtak a cikkeket ezerrel 2010 korul, aztan ezek megis ritkan valnak mainstreamme. Dart-ot is nyomatta a gugli, aztan hova jutott?

A node.js jol bizonyitja, hogy a legratyibb nyelvet is vissza lehet rangatni a reflektorfenybe egy uj keretrendszerrel/platformmal es ami egyszeru, az nagyszeru. Fontosabb, hogy hanyan ismerik mar az adott technologiat, mint hogy mennyivel jobb a bevalt eszkozoknel. Go egy teljesen uj platform, es ujra kene implementalni hozza azt a rengeteg bejaratott libet es felepiteni egy hasonloan "megbizhato" (enterspajz szemszogbol nezve kb = mar rengetegen hasznaljak, esetles openszosz, es sok komoly ceg nyujt hozza supportot) okoszisztemat ahhoz, hogy "levalthassa" pl. a Javat. Par cegnel ahol zsenik dolgoznak, ki tudjak hasznalni az uj nyelv adottsagait, de azert a fejlesztesek tobbseget eletunt enterspajzkoderek toljak akik nem fognak nedvesedni egy goroutine-tol.

Ha az MS folytatja az OSS fele fordulast, en a C#-nak sokkal nagyobb jovot joslok, mint Go-nak.

----------------------
"ONE OF THESE DAYS I'M GOING TO CUT YOU INTO LITTLE PIECES!!!$E$%#$#%^*^"
--> YouTube csatornám

Magam sem fogalmazhattam volna meg jobban. Amiért baromira fontos az ecosystem: holnapra kellene PDF-et, XLS-t, Reportot generálni több füllel, grafikonnal, csatlakozni
egy XY rendszerhez, amihez csak Z protokollal lehet, ami véletlenül sem valami szabványos JSON/XML, hanem mondjuk ASN.1 vagy hasonló szépség. Vagy nesze, itt egy WSDL,
kössed hozzá a rendszerhez, vagy itt egy JMS, csatlakozz. Egy csomó ilyen requirement van, és természetesen mindent meg lehet csinálni kézzel, csak ugye az idő faktor.

Ha a haverod azt mondja, hogy a csülökpörkölt szar, akkor te is utálni fogod? Gratulálok neked, ha ez így van:D Az ilyenre mondják, hogy birka:D Amúgy egy embert sem hallottam arról beszélni, hogy a Go miért is rossz, csak arról, hogy miért nem kellene használni, ez csak azt jelentheti, hogy nem is vagytok tisztában magával a nyelvel - előnyeivel, hátrányaival, és csak okoskodtok(no offense). Én olyan emberek véleményére lettem volna kíváncsi, akik használták, nem olyanokékra, akik nem, amint azt olvasni is lehet a topicban.

A kérdés az volt, hogy le fogja-e váltani a go a ... nyelveket. Nem azt jelenti, hogy jó vagy rossz nyelv-e a go, valójában ez édesmindegy. A kérdés az, hogy elég nagy rajongótábort maga mögé tud-e állítani, akik lekódolják azt a pár ezer könyvtárat, ami után azt mondja az ember, hogy bátran bevállal egy projektet.

"akik valszin 200x annyit tudnak, mint te": azt szeretném megkérdezni, hogy ezt honnan veszed? Mert az száz, hogy nem ismerlek:D, szóval honnan? Szerencsére, akiket én ismerek, nem ilyen csőlátásúak. Amúgy nem lett volna egyszerűbb azt írni, hogy az ismerősiem szerint blabla..., mintsem, hogy leszarozd, úgy, hogy még azt sem tudod, mi az, csak névből ismered?

Egy darab topic-ot csináltam az elmúlt 7 év alatt, nem tudom te mit néztél:D Amúgy is arra lennék kíváncsi, hogy miért nem szeretik a Go-t, ha ez tényleg igaz. Megoszthatnád, mivel pont ezért hoztam létre a topic-ot. Ha azért nem szeretik, mert nincs benne generics, vagy a hibakezelés miatt, akkor kiakadok:D

Ha teljesitemny kell, jobb mint ruby,cpython,perl..
Ha gyors kod iras kell, jobb mint a C, java, C++ ..

A ket vilag kozotti kombpromissum.
Sok ember a szokvanyos exceptionek hianyolja, ha
tudsz nelkule elni akkor valszoinuleg sok helyen tudod hasznalani.

Amit nem lehet megirni assemblyben, azt nem lehet megirni.

+1

Nagyon könnyű tool-okat készíteni vele, a google elég jó kis könyvtárakat készített hozzá.
A nyelvnek is elég sok jó tulajdonsága van, bár nekem egy pár dolog nem tetszik benne.

El tudom képzelni, hogy dominánsabb lesz, de nem hiszem, hogy bármit is le fog váltani.

"Sok ember a szokvanyos exceptionek hianyolja"
Why is exception handling bad?
Checked Exceptions are Evil

Szamomra try {..} .. csak synaxikai cukorka, semmi tobb.

A go exception kezelesevel minden happy volt amig csak a sajat kodommal kellett jatszani,
ami zavarassa tette egy olyan library hasznalata ami keszitett egy peldanyt minden errorbol indulaskor,
es ugyan azt a peldanyt minden fele parameter nelkul adta visza 8 helyen.
Valahol bug volt, az nem vitas, de hol? A library bugos vagy service amivel komunikalt.

Ha minden err forras egy kicsiben mas err-t add visza sokkal kovethetobb.
Telejes traceback elollitasa `draga`, valoszinuleg nem szeretnem ha mined errornak lenne teljes tracebackje, de egy egyedi integer vagy file:line_number eleg sok esetben bele ferne a budget -be.

Amit nem lehet megirni assemblyben, azt nem lehet megirni.

" a szokvanyos exceptionek hianyolja, ha tudsz nelkule elni akkor valszoinuleg sok helyen tudod hasznalani."

Ez nem arrol szol, hogy tudsz-e nelkule elni. Hanem az, hogy mar ez is plusz effortot jelent a valtasnal, hogy egy uj paradigmat kell megtanulnod, amit vagy be tudsz idomitani a jelenlegi szemleletmododba, vagy nem. De altalanossagban elmondhato, hogy azok a nyelvek lesznek nepszeruek, amelyek az ujdonsagaik mellett sok tampontot adnak a mas nyelvekrol atteroknek, peldaul exceptionoket, tulterhelheto operatorokat, dinamizmust, ilyesmit.

--
Blog | @hron84
Üzemeltető macik

(Elöljáróban: sosem programoztam benne, de kíváncsi voltam és elolvastam ezt a könyvet. Szóval azóta lehet, hogy javult, újabb változatok biztosan jelentek meg.)
Nekem nem tetszett meg, még ha vannak is benne jó dolgok. Valahogy a könyvtára sem, de kétségtelen, hogy a rendes generics hiánya sem tette vonzóvá. Nem látom, hogy hol van a use-case amit inkább ebben valósítanék meg mint C++-ban, vagy Scala-ban. (Mondjuk back-endre vsz. nem C++-ban kezdenék neki, de nem is RoR, Python vagy Node.js. Preferenciám: .NET vagy JVM lenne, így aztán a go elég gyorsan ki is esik.)
Ha esetleg lesz valami amit máson nagyon kényelmetlen lenne használni, akkor megfontolnám. De szerintem előbb-utóbb a C++-ban is megjelennek a gorutinok megfelelői lib-ként, magasabb szinten pedig meglepődnék ha még nem lennének (értelemszerűen valamivel lassabban), így erre sem sok esélyt látok. Egyébként pedig használnám wrapperen keresztül.
(Azt el tudom képzelni, hogy egy szűk rétegnek pont ilyesmire van szüksége, gondolom a Google sem véletlenül fejlesztette ki.)
Ami tetszik, azért nem fognék neki a használatához, nagy részét megtalálom más nyelvekben.

Ui.: A Dart sem tetszett. Egy ideje Paul Phillips Go-ban programozgat, twitteren az utóbbi napokban megosztotta a tapasztalatait is, pl.:
- standard logging cannot be queried.
- szokásos interface-ek hiánya (gist, interface check) - ez lehet, hogy csak megszokás kérdése, máshol is próbálkoznak ilyesmivel.
- type ascription hiánya

A nagy előnye a Scala-hoz képest, hogy gépi kódra fordul és sokkal kevesebb erőforrásokkal is beéri. Pl. cináltam egy microservice-t Scala-ban és Go-ban, ami egy alap REST backend volt, ezt Docker-rel csomagoltam. A Scala-s package mérete ~1GB, a Go-s ~20MB volt, a memória használatuk is hasonló, már nem emlékszem pontosan, de kb. két nagyságrend volt köztük. Ha fordulna gépi kódra a Scala is, akkor már nem tudnék semmi okot arra, hogy miért ne törne világuralomra :)

A C++-hoz képest pedig a jobb nyelv (nem megbántva a C++-osokat), és bizonyos dolgokhoz (pl. backend) a jobb könyvtár ellátottság.

Önmagában a program tényleg nem lenne olyan nagy.
Sajnos azért lesz olyan nagy, mert kell mellé a Java-t is csomagolni, mindent ami kell a futtatáshoz.
Lehet, hogy ezen lehet optimalizálni és akár 300-400MB-ra is le lehet csökkenteni a Docker csomag méretét, de nem hiszem, hogy annál sokkal kisebbre lehetne.

Szerk.: Ha meg még war-t is használnál, akkor még a szintén nem kis méretű alkalmazás szerver is kell mellé. Ennél azért jóval jobban jönnek ki a Scala-s alkalmazások, de azok is a Java miatt nagy hátránnyal indulnak a Go-hoz képest.

Nem véletlen írtam a Docker-t. Ez akkor játszik, ha az egyes microservice-eidet egymástól függetlenül akarod kezelni, deploy-olni, elindítani, leállítani, ... Mindegyik egy logikailag külön gép. Lehet tetszőleges nyelven írva mindegyik, lehet ugyanazon is. Ha valamelyiket át akarod írni másik nyelvre, akkor szabadon megteheted. Egy-egy microservice-t tetszőleges példányban futtathatod, fejlesztés közben pl. 1-2 példányban, production környezetben akár több százban is.

Ezeknek a keretrendszereknek pont az az előnye, hogy elég egyszer felrakni. Minek minden microservice mellé odacsomagolni a Java-t?

Ugyanez igaz pl. a .NET-re: ott van minden Windows mellett, egyszer, esetleg több verzió, de ennyi. Nem csomagolom oda minden szoftverhez.

----------------
Lvl86 Troll, "hobbifejlesztő" - Think Wishfully™

Furcsa véleménye van Kernighannek a kivételkezelésről. Szerinte káros, és ezért nem tervezte bele a Go-ba.
--
ulysses.co.hu

Nekem igazából nincs semmi bajom az exception-nal, mert azt mutatja, hogy itt bizony hiba LEHET és a fejlesztőt RÁKÉNYSZERITI, hogy csináljon valamit
a hibával. Ha lehetőséget biztosítunk a fejlesztőnek arra, hogy ne is foglalkozzon a hibával, akkor a fejlesztő jó eséllyel figyelmen kívül fogja hagyni.

Példa:

f, err := os.Open("filename.ext")

if err != nil {
log.Fatal(err)
}

Az első, amit kapásból ki fog a legtöbb fejlesztő hagyni, az az if err != nil... ráadásul nincs annál rondább, amikor egy kódrész tele van ilyen vizsgálatokkal.
Szerintem sokkal szebb, ha a hibajelzést blokkonként egy helyen tudom kezelni (pseudokód)

try{

f := os.Open("")

f.read()
f.write()

...

} catch (Exception e){
handleError(e);
}

Aztán.

http://blog.philipphauer.de/checked-exceptions-are-evil/

Ebben a cikkben az exception körüli problémákra azért jól rámutat a szerző, azonban nem vagyok meggyőződve arról, hogy a burkoló exception-nak Runtime
exception-nak kellene lennie. Szerintem ugyanúgy checked exception-nak kell lennie, beburkolva a belső exception-öket.

"Az első, amit kapásból ki fog a legtöbb fejlesztő hagyni, az az if err != nil... "
Az ilyenre általában jön warning és normális fejlesztési metodikánál a warning-okat sem illik a programban hagyni.

"ráadásul nincs annál rondább, amikor egy kódrész tele van ilyen vizsgálatokkal."
Ez így igaz, de ennél azért vannak szebb megoldási módok.
Pl. Scala esetén erre használható az Option, a Try (The Neophyte's Guide to Scala Part 6: Error Handling With Try), az Either (The Neophyte's Guide to Scala Part 7: The Either Type), a Validation (Functional Validation in Scala)

Egy jó slide a témában: Managing errors the right way in Scala.

Egy jó videó a témában: Bill Venners, Scalactic, SuperSafe, and Functional Error Handling, SF Scala

Ha az exception helyett egy alternatívát akarsz használni, akkor ugyanolyan csúnya, olvashatatlan lesz a kód, mint amit az if-ekre is írtál, pl.:


int value1;
int value2;

try {
  value1 = calcValue1()
} catch(CalculationException e) {
  value1 = 0;
}

try {
  value2 = calcValue2()
} catch(CalculationException e) {
  value2 = 0;
}

"Ha az exception helyett egy alternatívát akarsz használni, akkor ugyanolyan csúnya, olvashatatlan lesz a kód, mint amit az if-ekre is írtál, pl.:"

Ja, csak erre jobb helyeken szokott lenni egy kultúráltabb API, pl. C#-ban a bool Dictionary.TryGetValue(K key, out V value)

----------------
Lvl86 Troll, "hobbifejlesztő" - Think Wishfully™

szia!

A go-s dolgokról szeretnék két dologra reagálni:

Ha lehetőséget biztosítunk a fejlesztőnek arra, hogy ne is foglalkozzon a hibával, akkor a fejlesztő jó eséllyel figyelmen kívül fogja hagyni.

Apró javítás a példád alapján: ha deklaráljuk az err változót go-ban, akkor azt valahol fel kell használnunk, különben a fordító hibát dob és nem fordul le a program. (http://stackoverflow.com/questions/7059616/go-language-warnings-and-err…)

Lehet viszont trükközni a '_'-sal, pl.: f, _ := os.Open("a.pdf") (ez már lefordul), így valóban figyelmen kívül lehet hagyni dolgokat, viszont szerintem így se éri meg. Ha valami furcsa bug netalántán beüt, akkor meg lehet szerencsétlenkedni, hogy melyik lekezeletlen error banánhéjon csúszik el a program.

...ráadásul nincs annál rondább, amikor egy kódrész tele van ilyen vizsgálatokkal.

Okosan kell azt használni: http://blog.golang.org/errors-are-values

üdv

Szerintem semmire sem kell rákényszeríteni a programozót. A tankönyvek sajnos tele vannak a hibakezelés káros példáival. Tranzakciószintű hibakezelésre van szükség, miközben a részletekben előforduló kivételeket lokálisan nem szabad buzerálni.

Pszeudokód:


try{
	tranzakcio()
	//a tranzakciobol sokfele kivetel johet
	//pl. hianyzik valamilyen torzsadat
	//nincs fedezet az atutalasra
	//konkurens tranzakciok utkoznek, stb.
        //amikkel azonban lokalisan nem foglalkozunk
	
	commit
}
catch{
	abort
	//jelentes a hibarol:
	//hiba leirasa
	//callstack printelese
	//stack valtozok printelese
	//stb.
}

A kivételkezelés értelme (éppen arra van kitalálva, azért hasznos), hogy a hibát ne kelljen a hiba előfordulásának a helyén kezelni. Ha a hibát a hiba helyén akarjuk kezelni, akkor elég a return értékeket nézni. Tehát hangsúlyozottan lehetőséget kell adni a fejlesztőnek, hogy ne foglalkozzon a hibával.

--
ulysses.co.hu

Kiegészítés. Persze, foglalkozni kell a hibákkal a hiba helyén is: ott kell kivételt dobni. A kivétel elkapása másodlagos. Vagyis a kivételek dobálásában kell szorgalmasnak lenni, nem pedig az elkapásukban. Ez az, ami nem szokott kiderülni pl. a Jáva tankönyvekből.

Nagyon opponálni nem szeretnék, de az érem másik oldalát megmutatni azért mégis: "szórakoztató" eseménysorok szoktak abból kijönni, amikor a "valami szar" hibajelentés nyoma a logban egy félméteres, lekezeletlen kivétellánc, amely minden állomásának fejlesztője az előzőére mutat, hiszen az ő modulja biztosan tökéletes, amit az is bizonyít, hogy máshol (még) nem volt gond.

Ez attol fugg, hogyan definialod a "nem foglakozik" fogalmat.

A C-stilusu hibakezeles eseteben, ha a fuggveny ervenytelen ertekkel tert vissza, attol meg a program futott tovabb, pontosan ezert letezik a null pointer hiba intezmenye.
A Java-stilusu hibakezeles eseteben, ha a fuggveny kivetelt dob, akkor a program futasa vagy megszakad, vagy a kotelezoen megirando kivetelkezelo kodreszletre ugrik. Ilyen modon tehat a kivetelek nem adnak lehetoseget a hiba ignoralasara, csak arra, hogy a keletkezo hibakat kotegelten kezeljuk, valahol, ahol ez celszeru.
--
Blog | @hron84
Üzemeltető macik

Tévedés, hogy nem lehet ignorálni a kivételt. Éppen eggyel lejjebb mutattam meg, hogyan kell ignorálni. A lényeg, hogy csak olyan hibát szabad elkapni, amit értelmesen tudunk kezelni. Ha egyáltalán nem kapjuk el a hibát, akkor a Jáva kiírja a callstacket, és kilépteti a programot. Ez a default hibakezelés. Sok esetben éppen ez kell.

--
ulysses.co.hu

Oooo, nem. Attol, hogy atfogalmazod, meg nem lesz feltetlen igaz, amit mondasz. Ignoralni semmikeppen sem lehet, hogy ott kivetel tortenik, tortenhet, bizonyos szempontbol az is kivetelkezeles, hogy a metodus szignaturajaba belerakod, hogy throws LofaszException. C-ben ellenben igenis lehet ugy hibat kapni, hogy nem foglalkozol vele, hiszen az errno valtozo figyelese csak ajanlas, ha nem olvasod ki az errno-t vagy a visszateresi erteket nem ellenorzod, akkor nem tortenik semmi, a program fut tovabb. A Java-ban ellenben valaki biztos, hogy ordibalni fog, hogy he, itt hiba tortent, tessek valamit csinalni, es ha kezeletlen kivetel tortenik, az egesz program megall, mint a szog.

Szerintem a hiba ignoralasa azt jelenti, hogy nem foglalkozunk a hibaval. Ha viszont mar a fordito ordibal, hogy itt hiba lehet, akkor mar egyszeruen muszaj foglalkozni vele, akar olyan szinten is, hogy csak tovabbengeded, de mar ott van a kodban mindenki szamara lathatoan, hogy itt valami lehet.
--
Blog | @hron84
Üzemeltető macik

"a megkritizált cikk írója egyszerűen nem tudta, hogyan kell továbbengedni a hibát, helyette baromságot ajánlott"

Olvastad egyáltalán a cikket?
Szerinted ez mi? Itt elkapja az exception-t vagy továbbengedi?


private String getContent(Path targetFile) throws IOException{
   List<String> lines = Files.readAllLines(targetFile);
   String content = Joiner.on("").join(lines);
   return content;
}

Ha érdemben nem tudsz kezdeni a hibával semmit a kivétel helyén, miafasznak elkapni?

Ha a program érdemben nem tud mit kezdeni a hibával, minek tovább futtatni a programot?

Ha mondjuk egy property settere az adat validálásakor dob egy InvalidArgumentExceptiont, azzal ott a propertyben mit akarsz kezdeni? Megnyitsz dialógusablakot, hogy hé, elbasztad egy setterben? :)

----------------
Lvl86 Troll, "hobbifejlesztő" - Think Wishfully™

null pointerről egy pár gondolat: csak én lennék az egyetlen, aki a null pointer, mint érték fogalmat eltörölné a francba és az SQL-hez hasonlóan mint "semmi" definiálnám? Ugyanígy párhuzamosan bevezetném a referencia típusokra (pl. C# esetén), hogy alapból nem lehet null, csak ha kifejezetten deklarálom, hogy nullable?

Egy halom if (foo == null) throw new ArgumentNullException("foo"); -t meg lehetne spórolni...

----------------
Lvl86 Troll, "hobbifejlesztő" - Think Wishfully™

Nem, nem te vagy az egyetlen. (viszont én pl. Java-ban is szívesen a C#-ban levő nullable type-okat)

Szerk.: Ezt pl. a PHP még jól is csinálta meg, ott ez szépen működik a Type hinteknél [igaz, hogy nem fordítási időben szól, hogy itt nullable-ként akarsz egy non-nullable típust használni, de kb. nincs fordítási időben típus, úgyhogy ez nagyjából tárgytalan]

BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)

Értelek.

De két eset lehetséges: NEM JÖHET null, ekkor mondhatjuk, hogy nem nullable típust adunk vissza. Ha pedig JÖHET null, akkor meg ígyis-úgyis le kell kezelni, tökmindegy, hogy

if (ref != null)

, vagy

case maybeVal of Just val | Nothing

.

Ez szemantikai szinten dől el.

Ez esetben pedig mégiscsak az Maybe/Optional a barátod, mert az pont ebbe a szemantikai képbe illik bele, ahogy a funkcionális nyelveknél mindenképpen lesz "visszatérési érték", azaz a kifejezésnek értéke.

Nagyon jó felvetés!
Scala-ban erősen ellen javallt a null használata, helyette az Option-t kell használni.
Mivel a Java oldalról könnyen jöhetnek null értékek, ezért könnyen lehet ezeket Option-né konvertálni, pl. Option(foo), ennek az értéke Some(foo) lesz, ha foo nem null és None, ha null.
Az Option-t nagyon könnyű kombinálni más függvényekkel, így nem sokkal nehezebb használni, mint az eredeti értéket.
Pl. ha foo számot tartalmazhat, vagy null-t, akkor a következő kóddal kiírathatjuk a felét, ha kettővel osztható egyébként pedig hibaüzenetet:


val result = Option(foo)
  .filter(_ % 2 == 0)
  .map(_ * 2)
  .map("A megadott szám fele: " + _)
  .getOrElse("A megadott szám hibás!")

Tehát a hibakezelés kapcsán az egyik előnye, hogy kiváltja a NullPointerException-öket, ráadásul könnyebb javítást adni a hibákhoz.

A hibakezelésnél a másik előnye, ha egy függvény érvénytelen bemenő adatokat kap, vagy a megadott bemenő adatokhoz nem tartozik kimenet, akkor is remekül használható.
Ha pl. van egy átlag számító függvényünk, ami egy listában megadott számok átlagát számolja, akkor üres listára vagy IllegalArgumentException-t, vagy ArithmeticException-t (0/0 végett) adna. Ehelyett, ha Option-t adunk vissza, akkor a függvényünk remekül kombinálható marad más függvényekkel és nem kell a try-catch-ekkel bajlódni.


val result = mean(numberList)
  .filter(_ % 2 == 0)
  .map(_ * 2)
  .map("A megadott számok átlagának a fele: " + _)
  .getOrElse("A megadott számok átlaga nem páros, vagy nem volt szám megadva!")

Hát, én programozok Go-ban egy ideje, és nagyon szeretem!

Az, hogy nem kivétel kezelés van, megfordítja a gondolkodást: nem azon kell gondolkodni, hogy "jé, itt dobhat valami kivételt, akkor engedjem tovább, vagy naplózzam és szálljak ki?", hanem rákényszerít arra, hogy MINDEN művelet adhat hibát, amit OTT HELYBEN le kell kezelni valami módon.
De ha pl. ír egy fájlt az ember, akkor egyszerűsíthet, használhat egy "errWriter" típust, ami implementálja az io.Writer interfészt, (tehát mindenütt használható ahol az használható), amiben "megragad" a hiba, és elég a végén ellenőrizni, hogy volt-e.

Amúgy meg pont a Go egyszerűsége és a gofmt (egy, kanonikus formázás) miatt nagyon egyszerű tool-okat írni a nyelvhez: pl. a github.com/kisielk/errcheck program kilistázza, hol nem kezeljük le a visszaadott hibákat.

De tény, hogy a gondolkodás megváltoztatására van szükség: a kezdők kérdéseire 99%-ban jó válasz az, hogy "először is kezelj le minden visszaadott hibát!".

Lehet, hogy nehezebb úgy programozni, hogy minden függvényhívásnál el kell gondolkodni, hogy milyen hibák jöhetnek, és arra milyen választ érdemes adni, de SOKKAL robosztusabb programok születnek!

Az, hogy nem kivétel kezelés van, megfordítja a gondolkodást: nem azon kell gondolkodni, hogy "jé, itt dobhat valami kivételt, akkor engedjem tovább, vagy naplózzam és szálljak ki?", hanem rákényszerít arra, hogy MINDEN művelet adhat hibát, amit OTT HELYBEN le kell kezelni valami módon.

Nem értem ezt a logikát. Miért kéne MINDEN művelethez feltételezni, hogy adhat hibát? Vannak műveletek, amik nem adnak hibát és kész. Amik adnak, annak ott van a deklarációjában a checked exception. Rá vagy kényszerítve hogy elgondolkozz rajta mit kezdesz a dobott kivétellel. A gondolkodás nélkül írt kód következményeitől a golang sem véd.

Java-ban is vannak patternek, mint a template pattern, az új try/catch syntax az auto closable resource-okkal, stb, de baromság ezen rugózni, egyszerűen két különböző nyelv különböző módon oldja meg ugyanazt a problémát. Az hogy jobb-e a golang mint mondjuk a Java vagy sem, sok tényezőtől függ, és sokszor nem is a jobbik nyelv terjed el. Hiányos ismereteim alapján nem tűnik túl izgalmasnak a golang. Most sokan ráugrottak, pár év és kiderül mennyire életképes.

"Rá vagy kényszerítve hogy elgondolkozz rajta mit kezdesz a dobott kivétellel. A gondolkodás nélkül írt kód következményeitől a golang sem véd."

A topikban előforduló hozzászólások alapján nekem olyan érzésem van, hogy sokan úgy gondolják, hogy a fordító által kikényszerített exception az sokkal jobb, mint ha a doksiban lenne leírva, hogy ez a művelet ilyen hibát adhat. Ezt szerintem nevezhetjük gondolkodás nélküli programozásnak. Éppen az ilyen gondolkodás nélküli, rákényszerített programozásból adódik sok meglepetés is.
Pl. a doksiban le van írva, hogy egy művelet nem thread safe, szinkronizálni kell, ha több szál is elérheti. Ezt a fordító nem kényszeríti ki. Ha valaki nem olvassa el a doksiját, akkor ezt nem tudja és esetleg hibásan fogja használni. Pl. a SimpleDateFormat is ilyen, nem megfelelő használatával évente párszor előforduló rejtélyes hibák jöhetnek elő.

A probléma általános: te feltételezed, hogy a könyvtárhoz lévő doksi 1, helyes, 2, aktuális. Valójában pedig nem. Lásd példám, sehol nem írták, hogy runtime exception-t
dob. Éppen ezért, a kódban IS KELL jelezni checked exception formájában, ha várható az adott metódustól exception.

Nem, nem jol erted. En a konyvtarak API-jat nezem at (ami nem egyenlo a forraskoddal!), hogy dob-e RuntimeExceptiont. Ugyanis a nem opensource konyvtaraknak is van API-juk, amit barmelyik random IDE megmutat neked.

A thread safety pedig olyan dolog, amire csak akkor van szukseg, ha multithreading alkalamzast fejlesztesz, nagyon sok alkalmazas azonban nem ilyen, vagy kezelheto ugy, mintha nem ilyen lenne (az altalad irt kod ugyanazon a szalon belul fog vegrehajtodni).

Egyebkent pedig teljesen mindegy, hogy mit mondasz, a doksiban az van benne, amit beleirnak, ami vagy igaz, vagy nem, vagy teljeserteku vagy nem. Ha azonban az API feluletbol az latszik, hogy itt dobodhat LofaszException, akkor nincs is szukseg a dokumentacio ellenorzesere, hogy ez tenyleg igy van-e.
--
Blog | @hron84
Üzemeltető macik

Nem is tudtam, hogy hrgy84 == sz332!

"En a konyvtarak API-jat nezem at (ami nem egyenlo a forraskoddal!), hogy dob-e RuntimeExceptiont. Ugyanis a nem opensource konyvtaraknak is van API-juk, amit barmelyik random IDE megmutat neked."

Így van, igazad van, az nem egyenlő a forráskóddal, az a doksival egyenlő. (szerk.: amibe azt írnak, amit akarnak)

"Egyebkent pedig teljesen mindegy, hogy mit mondasz"

Igen, ezt kezdem észrevenni.

" az a doksival egyenlő."

Bocs, muszaj megkerdeznem, hogy programoztal-e mar _valaha_ Java-ban, vagy dolgoztal-e mar valaha zart forrasu Java konyvtarral. Ugyanis nagyon kevered a fogalmakat, ami pedig nagy hiba.

Az API konyvtarak ugyanolyan Java fajlok (nem dokumentacio), mint a zart forrasu cucc, csak epp implementacio nelkul. Ezt a legrosszabb esetben az IDE maga allitja elo a zart forrasu bytekod elemzese es reszleges visszaforditasa utan. Ez nem keverendo ossze a disassemblalassal, ilyenkor az IDE semmi egyebet nem csinal, mint betolti a JAR fajlt, es reflection-nel visszakeresgeli az egyes osztalyok/metodusok szignaturajat, es ezt megjeleniti ugy, mintha egy ervenyes Java forraskod lenne, csak eppen ures metodustorzzsel. Ha jol tudom, akkor a privat metodusokat nem is lehet ily modon eloszedni, tehat csak azt fogod latni, amit latnod is kell egyebkent is.

Egyes zart forrasu konyvtarak fejlesztoi elebe mennek ezeknek a problemaknak, gondolnak azokra, akiknek nincs szerencsejuk olyan IDE-t hasznalni, ami ezeket elo tudja allitani, es maguk is nyujtanak API JAR-okat, amelyeknek a forrasa is publikus, am azok tovabbra sem tartalmaznak semmilyen implementaciot, csak a metodus szignaturakat es a JavaDoc kommenteket.

Ez az, amit en API-nak hivok (a dokumentaciot meg dokumentacionak hivom), es ez az, ami valojaban szamit, ugyanis ebbe nem irhatnak azt, amit akarnak, mert ha mas szignaturahoz forditom a kodot, mint a runtime konyvtar, amit hozzakot futtataskor a JVM, akkor egy NoMethodError-ral elszall az egesz a francba.
--
Blog | @hron84
Üzemeltető macik

"Bocs, muszaj megkerdeznem, hogy programoztal-e mar _valaha_ Java-ban, vagy dolgoztal-e mar valaha zart forrasu Java konyvtarral. Ugyanis nagyon kevered a fogalmakat, ami pedig nagy hiba."

Több, mint 10 éve dolgozom Java-val és szerintem nem én keverem a fogalmakat.
Valóban az IDE-k ki tudják szedni a szignatúrát a class-okból, de azzal nem tudsz meg semmit se a RuntimeException-ökről, sem a thread safety-ről. Ezeket az infókat a Javadoc részbe írják kézzel, tehát oda bármi kerülhet, lehet a javadoc részben egy RuntimeException, ami nincs is a kódban, illetve fordítva.

"Javadoc is how Sun documents in the core application programming interface (API) of Java (see http://java.sun.com/javase/6/docs/api/), and how you can document the APIs you provide to other software developers."

Oké, tehát, a magam részéről beszélek (nem vagyok hrgy84)

Nekem egyes egyedül a RuntimeException nem tetszik. Azért nem tetszik, mert sem az API-ból:

interface X{

void myMethod();

}

nem fog kiderülni, hogy a metódus dob-e exception-t, sem pedig a hozzá kapcsolódó dokumentációból, mert előfordulhat, hogy lefelejtik.

Ezzel pedig az a gond, hogy a hiba kezelése le fog maradni, és majd csak testing, vagy rosszabb esetben production környezetben fog előjönni, azt meg nem szeressük.

A téma apropója pedig az volt, hogy a GWT-ben a JSON parsoló függvény egy runtime exception-t dob, ha nem sikerül a parsolás, de ezt elfelejtették beleírni a dokumentációba, és az API-ba (függvény szignatúra) sem jelent meg (hiszen runtime exception....)

Nekem egyes egyedül a RuntimeException nem tetszik.
...
Ezzel pedig az a gond, hogy a hiba kezelése le fog maradni, és majd csak testing, vagy rosszabb esetben production környezetben fog előjönni, azt meg nem szeressük.

"Gyárilag" a runtime exception-t nagyjából erre találták ki, pl. teljesen korrekt use case rá, hogy a JVM dob egy OutOfMemoryException-t, ami gyakorlatilag egy mindig-lehetséges kivétel, de azért nem kéne minden method signature-be bevésni.

Hogy rosszul használják (lásd a szál indító hozzászólásban az írást... dafuq, vagy a JSON parse példát) az már más kérdés.

BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)

Forraskod alatt itt en arra probaltam reflektalni, hogy a kollega azt feltetelezi, hogy ha mondjuk en servletet irok, akkor elobanyaszom teszemfel a GlassFish forraskodjat, es nekiallok tanulmanyozni a servlethez szukseges metodusok felepiteset. Amire en gondolok, az viszont a servlet-api, ami bar technikai ertelemben forraskod, megsincs semmi koze mondjuk a GlassFish servlet kezelesehez, hiszen ez egy generikus API, tenyleges implementacio nelkul. Megis, nekem eleg csak a servlet-api forrasat elolvasnom ahhoz, hogy tudjam, a servletemben milyen Exceptionokre kell felkeszulnom. Azt, hogy ez a GlassFish implementaciojaban is igy van, azt persze kulonbozo egyeb szabvanyok garantaljak nekem, azonban e helyt ebbe nem akarok reszletesen belemenni, gondolom ha te kodolsz Java-ban, sejted mire gondolok.

Ami itt valojaban a lenyeg: a Java fejleszto nem a dokumentaciot veszi eloszor elo, hanem a metodus szignaturajat, es abbol talalja ki, hogy az implementacio milyen RuntimeExceptiont tud dobni, es ehhez nincs szuksege az implementacio konkret forrasara.
--
Blog | @hron84
Üzemeltető macik

"a Java fejleszto nem a dokumentaciot veszi eloszor elo, hanem a metodus szignaturajat, es abbol talalja ki, hogy az implementacio milyen RuntimeExceptiont tud dobni, es ehhez nincs szuksege az implementacio konkret forrasara."
Értem. Java fejlesztő vagyok, de nem tudom interface-ben deklarált metódusból megmondani, hogy a ki tudja melyik implementációja milyen RuntimeException-t fog dobni. Leírod hogy kell?

Ha ott van a metodus szignaturajaban, hogy "throws LofaszException" akkor LofaszException-t dobhat az implementacio. Ha nincs ott a "throws" clause, akkor is dobhat ugyan akarmilyen exception-t, de az egyertelmuen hiba, hiszen a compiler mar az o oldalan is warningol, hogy figyelj-figyelj, te itt dobsz egy exception-t, de a metodus szignaturajaban ez nincs benne. Az implementacio nem implementalhat ugy fuggvenyt az interfeszrol, hogy mas (tobb vagy kevesebb) exception-t jelez a szignaturaban, mint ami az interfeszen van, mert akkor nem ugyanaz a metodus lesz.

Persze, vannak olyan fejlesztok, akik ezeket ignoraljak, de azzal nem sokat tudsz tenni. Mondjuk ugyanez a fejleszto eleg jo esellyel a javadoc-ba se fogja beletenni, hogy o most akkor _neha_ dob LofaszException-t is.

Figyelj, ami nincs ott a metodus szignaturajaban, arra nem kell feltetlenul keszulni. Mas kerdes, hogy ha mondjuk korabbi tapasztalatbol tudod, hogy az adott metodus altalaban dob ilyen meg olyan exceptionoket, de alapvetoen szerintem a Java-ban a metodus szignaturaja a contract, egy ismeretlen library eseteben kizarolag ahhoz kell tartani magadat. A dokumentacio az csak olyan, mint a MAV menetrend - tajekoztato jellegu, de lehet, hogy semmi koze a valosaghoz.
--
Blog | @hron84
Üzemeltető macik

A rengeteg értek hozzá rizsa helyett inkább ne szólnál hozzá olyan dolgokhoz amihez nem értesz. Vagy nézz utána ha nem bírod megállni. Runtime exceptionökről beszéltél.

"Ha nincs ott a "throws" clause, akkor is dobhat ugyan akarmilyen exception-t"
Ez sem igaz.

Folytassuk még?

mint ha a doksiban lenne leírva, hogy ez a művelet ilyen hibát adhat. Ezt szerintem nevezhetjük gondolkodás nélküli programozásnak.

Nagyon jó, akkor (akár doksiból) sorold már fel az összes lehetőséget, amin egy képzeletbeli Authn.auth(...) megborulhat. Vagy nagyságrendileg tippeld meg, hogy hány soros lenne az a struktúra, amiben admin-barát módon lehetne tárolni, hogy hol borult meg, hogy az admin tudja, hogy melyik auth forrást kell ellenőriznie?

BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)

Tegyük fel, hogy írsz egy könyvtárat, ami felhasználó-azonosítást végez. És plug-in-rendszerűen cserélhető mögötte az azonosítási réteg, egyszerű "felsorolom a userek a forráskódban" (itt ugye nagyjából csak a kifogytunk-a-memóriából és a kicsapatták-a-220-ból-a-gépet hibalehetőségek vannak) megoldástól felfelé bármeddig - teszem azt egy krb single sign-ont használó SAML mondjuk LDAP back-enddel (a SAML szerver nem érhető el, DNS hiba van, SSL hiba van a kliens és a SAML szerver közt, SSL szerver van a SAML és az LDAP szerver közt, a kliens nincs negotiate-re beállítva a SAML szerverhez, a krb szerver lehalt, az LDAP tanusítványa lejárt, bárhol hálózati hiba van, redundáns rendszereknél kidőlt valamelyik node, de a DNS round robin vagy a load balancer oda küldi, a negotiate auth bárhol elhasalt, time-outolt a session a SAML-en az auth előtt/alatt stb.).

A kliens így azon túl, hogy "nem megy az auth, sorry" sok mindent nem tud kezdeni (mert nem tudja, hogy milyen providerek vannak egyáltalán és nem tudja, hogy melyik provider hány féleképpen tud elhasalni). Egy adminisztrátornak viszont fontos információ lehet, hogy pl. az SSL hiba miatt nem tudott az LDAP-hoz csatlakozni. Itt akkor két lehetőséged van: "helyileg" kezeled ezeket az eseteket, így a hibakezelést (ami így muszáj, hogy kimerüljön a naplózásban) ki kell, hogy merüljön abban, hogy kivésed a logba, hogy gond van, aztán visszaadsz egy random hibakódot, jó esetben hibaüzenettel együtt - amit utána még feljebb adogatsz, vissza a kliens kódig, akkor kiírja, hogy "nem megy az auth, sorry" és REMÉNYKEDIK, hogy a megfelelő modul kivésett bármi használható az error logba. A másik lehetőség, hogy szépen kitalálsz egy (minden valószínűség szerint rekurzív) struktúrát arra, hogy a hibákról a lehető legtöbb információt visszaadj (hiba fajtája, kódja, leírása, paraméterei [melyik szerver melyik portjánál ment szét egy SSL handshake]) - mindezt úgy, hogy gyakorlatilag bármilyen elborult implementációra előre fel kell készülnöd.

Ha ugyanezt kivételekkel oldod meg: ahol felmerül a hiba (ez ugye a plug-in lesz, neki sok hibát nem feladata kezelni) a tényleges hibát (legyen egy SSLException, de ugyanígy lehetne SQLException, ...) becsomagolod egy AuthException-be, és feldobod az auth alrendszernek. Ő ott szépen (contracttől függően) kivési a logba a hibát és - függően az auth és a kliens közti contract-től - vagy továbbdobja a kliensnek vagy visszatér egy false-al.

A kliens kódod nagyjából ugyanúgy fog kinézni, gyakorlatilag szintaxis-beli különbség lesz benne: próbáljunk meg auth-olni, ha bármi gond van, szóljunk a usernek, hogy "bocs, baj van, próbáld később". Az igazi különbség az egyes plug-inek kódjában lesz, akiknek nem kell naplózással foglalkozniuk (mert nem feladata... ha biztosít debug logging, az jó, de a "rendes" naplózás ne az ő felata legyen - lásd jó pár PAM modult, amiknek van egy debug kapcsolója), nem kell nekik újrakitalálniuk a spanyol viaszt (a fentebb emlegetett rekurzív struktúra, amit arra használnánk, hogy egy felmerülő és komoly problémát jelezzünk vele engem nagyon emlékeztet az Exception-ökre), ráadásul ugyanúgy, ahogy így a kliens kódtól egy AuthException-nel többé-kevésbé elabsztraktáltuk a teljes auth alrendszert, a plug-inek kódjától is ugyanígy el lehet választani a teljes pl. fájlrendszert (mert mondjuk ki mondta, hogy a .htpasswd fájlunk helyi fájlrendszeren van, és nem egy pár száz km-re levő adatközpontban egy cluster fájlrendszeren...)

És gyakorlatilag ez a lényeg: azzal, hogy származtatható kivételeket használsz, nyersz két fontos dolgot: mindenképp kényszerítetted a kliens kódodat legalább a hiba típusának a definiálására (*) és gyakorlatilag mindenki elől elrejtetted az összes szart, ami kártyavárként rá dőlhet - és előbb-utóbb rá is fog. Viszont mivel legalább egy hiba típust tudsz, még ha vállalhatatlanul van is megírva az éppen megboruló provider-ed, az admin el tud indulni valahol.

(*) pl. így:


abstract class AuthException extends Exception {}
interface AuthPlugin {
 public boolean auth(...) throws AuthException;
}

Aztán persze mindig lesznek barmok, akik simán dobálnak a plug-in kódjukból unchecked exception-öket, mert hát csak...

BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)

Ez már így tényleg kimerítő, így már könnyen megértettem! :) Talán egy kicsit kevesebb is elég lett volna, de köszi a fáradozást!
Akkor én is megpróbálok ilyen kimerítő lenni! ;)

Ez nem igazán az idézett részre vonatkozik.
Ott én csak annyit állítottam, hogy sokan úgy programoznak, hogy az IDE és a fordító által adott szolgáltatások segítségével kvázi gondolkodás nélkül programoznak. Beírja az objektum nevét, pont, feljönnek az objektum metódusai, kiválaszt egyet, az IDE kitölti a paraméter listát, kicseréli a paramétereket, ezután ha jelez a "fordító", hogy exception-t kell lekezelni, akkor bevágja egy try - catch blokkba, ha nem, akkor megy tovább.
Ha az adott metódust akár először vagy másodszor látja, akkor sem olvassa el a doksiját, és fogalma sincs, ha ott jöhet RuntimeException, vagy az nem thread safe.

Amit írsz az inkább azt a kérdést feszegeti, hogy checked exception, runtime exception vagy (pl. a Go féle) többszörös érték visszaadás a jó.

  1. Checked Exception. Előnye, hogy szól a fordító és kikényszeríti, hogy valamit kezdj vele.
    Hátrányai:
    • nehezen olvashatóvá teheti a kódot
    • nehéz refaktorálni
    • inkonzisztens állapotot hagyhat maga után
    • nehéz újrafelhasználni
    • nehéz kombinálni más dolgokkal (compose)
  2. Runtime Exception: Előnyei:
    • könnyű refaktorálni
    • könnyű újrafelhasználni
    • könnyű kombinálni más dolgokkal (compose)

    Hátrányai:

    • nehezen olvashatóvá teheti a kódot
    • nem szól a fordító és kényszeríti ki, hogy valamit kezdj vele
    • inkonzisztens állapotot hagyhat maga után
  3. Többszörös érték visszaadás. Előnyei:
    • könnyebben olvasható a kód
    • nem hagy inkonzisztens állapotot maga után
    • könnyű refaktorálni
    • könnyű újrafelhasználni
    • könnyű kombinálni más dolgokkal (compose)

A fentiek fényében a kérdésedre a válasz:

  1. Checked exception. Ezt leírtad, hogyan lehet kezelni vele.
  2. Runtime exception. Ugyanaz, csak a doksiba kerül bele az exception leírása, nincs kikényszerítve.
  3. Többszörös érték visszaadás. Visszaadod az értéket és az adott exception-t is, tehát nem dobod az exception-t, hanem visszaadod úgy, mint más értékeket is.

Ahogy írtad is korábban a Runtime exception-t nem erre találták ki, hanem arra, hogy a programozási hibákról adjon minél gyorsabban visszajelzést, ezek elkerülése programozással megoldható. Sokan mégis azt választják egyéb esetekben is az előnyei miatt.

A többszörös érték visszaadást a Java-ban nem igazán használják, inkább a két rosszabb közül választanak, köszönhető ez annak is, hogy a Java nyelvből hiányoznak az ezt támogató elemek. Go-ban nincs is más, Scala-ban (az exception nem kikényszerített) javasolt inkább a többszörös érték visszaadást választani az előnyei miatt, ráadásul nagyon meg van támogatva a kezelése.

Próbálok sorban:

Checked Exception.

nehéz refaktorálni

Ha rosszul volt tervezve (vagyis simán áteresztette magán a belső checked exception-öket, vagyis implementációs részeket leakelt), akkor valóban. Ha viszont saját Exception osztályt használt burkolt belső kivételekkel, akkor (leszámítva az esetet, hogy tudod garantálni a hibamentességet és meg lehet szüntetni az Exception-t :) ) nagyjából ugyanannyi refaktorálni, mintha runtime exception-öket dobáltál volna vagy az exception-t visszaadtad volna.

inkonzisztens állapotot hagyhat maga után és Többszörös érték visszaadás.
nem hagy inkonzisztens állapotot maga után

(Egyébként ha immutable objektumok felett pure function-öket használsz, ez kb. tárgytalan)
Mennyiben segíti a többszörös érték visszaadás az inkonzisztens értékek visszaadását. Ha egy metódus törzsében "középen" kapsz egy hibát, a többszörös érték visszaadás rákényszerít-e, hogy visszatérj egy konzisztens állapotba? Valóban, ugyanígy egy checked exception sem kényszeríti ki a konzisztenciát, viszont (ha csak nem nyeli le a kód az Exception-t), legalább garantáltan propagálódik az értesítés, hogy nem feltétlenül konzisztens.

nehéz kombinálni más dolgokkal (compose)

?
Jó esetben a más dolgoknak is van egy saját kivétele a signiture-ben, egy laza mozdulattal beágyazod a külső kivételbe, és jónapot.

Többszörös érték visszaadás.

könnyebben olvasható a kód

Szerintem nem. Egy kivételnél szépen látod, hogy meddig tart a try blokk, tudod, hogy azon a szakaszon fordulhat elő valami hiba - aminek a kezelése elkülönül a fő ágtól. Linkeltétek a "használjunk wrapper függvényt, ami feljegyzi, hogy volt-e már hiba és ha igen, menjen át noop-ba" megoldást: azon a kódon nagyon szépen látszik, hogy - pont, mint C-ben, jobb eszköz híján - a hibakezelés bekerült a fő ágba.
Ráadásul a példa meglehetősen egyszerűsített, mivel egy homogén művelet hibáit veszi csak figyelembe. Ugyanez mondjuk annál, hogy beszúrsz egy rekordot a DB-be, és küldesz egy értesítő e-mailt már lényegeseb bonyolultabb lesz (és függetlenül attól, hogy hogyan szervezed a kódod, elrejtheted akár 15 hívás mélységben is):
ha a rekord beszúrásánál hiba volt, akkor tranzakció vissza, nem küldünk levelet.
ha a levél kiküldésénél hiba volt, akkor adatbázis tranzakció vissza.
ha nem volt hiba, örülünk.

Ez ugye Exception-ökkel


try {
 db.beginTransaction();
 db.insert(foo);
 mail.send(bar);
 db.commit();
} catch (MailException x) {
 db.rollback();
} catch (DBException x) {

}

A fő ágad és a hibakezelésed szépen elkülönül. Ha nincs kivételkezelésed (pszeudó-kód, mert nem ismerem a go-t)


db.beginTransaction()
err_db = db.insert(foo)
if err_db != nil
  err_mail = mail.send(bar)
  if err_mail != nil 
     err_db.rollback()
  else
     db.commmit()
  endif
endif

A kód komplexitása ugyanaz maradt (4 lehetséges út van), viszont az utóbbinál kénytelen vagy mind a négy utat expliciten megjeleníteni a fő ágadban. Ami a tranzakción belül műveletek számával szépen (talán) exponenciálisan fog nőni (tegyük bele a kötelező naplózást egy audit naplóba, ami ha nem sikerül, vissza kell vonni a tranzakciót - a mail mondjuk itt már keresztbe tesz, mert annak van egy visszavonhatatlan mellékhatása; vagyis azt kell mindig utoljára hagyni), ez újabb elágazást fog jelenteni. Exception-nel ez egy újabb audit.log('foo') hívás, és egy újabb catch block. A komplexitás ott is ugyanaz, _kód szinten_ mégse nő az elágazások száma.

könnyű refaktorálni

Mennyivel könnyebb, mint a check exception-nél? A method signature-t mindenképp módosítanod kell, az meg nagyjából lényegtelen, hogy a visszatérési érték változik, vagy a dobott kivételek listája.

BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)

Az alap Java-ból hiányoznak azok az elemek, amikkel könnyen compose-álhatunk dolgokat, a go-t nem ismerem nagyon mélyen, így Scala-val mutatom az utolsó példát, hogyan lesz egyszerű és compose-ált a hibakezelés:


db.beginTransaction
Try(db.insert(foo)) flatmap
  Try(mail.send(bar)) map
  db.commmit recover
  db.rollback

Ha egy kicsit meg akarnánk bonyolítani a helyzetünket, akkor tegyük fel, hogy, ha nem sikerül az adatbázis beszúrás, akkor egy másik beszúrással próbálkozunk, illetve, ha nem megy el az email, akkor egy másik email küldéssel próbálkozunk. Ezt try-catch-el már kicsit bonyolultabban lehet csak megoldani.


db.beginTransaction
Try(db.insert(foo)) orElse Try(db.insert(foo2)) flatmap
  Try(mail.send(bar)) orElse Try(mail.send(bar2)) map
  db.commmit recover
  db.rollback

"Idézet: könnyű refaktorálni. Mennyivel könnyebb, mint a check exception-nél?"

Annyival, hogy itt egy fajta hiba van (Error a Go-nál, Throwable a Java, Scala-nál). Ha mindig csak Throwable-t dobsz (ez van a szignatúrában), akkor ott is könnyű, de ugye nem ez az ajánlott megoldás checked exception esetén.

Azért ugyanez szerintem bőven megoldható egy-két saját util osztállyal és lambdákkal [minusz of course a szintaxist, amitől kb. még mindig fáj a fejem].

Annyival, hogy itt egy fajta hiba van (Error a Go-nál, Throwable a Java, Scala-nál).

És az előny? (és továbbra sem látom, hogy egy jól megtervezett libnél milyen gondot okozhatnak refaktorálásnál a checked exception-ök.)

BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)

"használjunk wrapper függvényt, ami feljegyzi, hogy volt-e már hiba és ha igen, menjen át noop-ba"
És ez miért nem használható ebben az esetben is? (mail-os db-és példád)
Példa: http://play.golang.org/p/_nagsbM12L

Lényegi kód:


type ErrorManager struct {
	Err error
}

func (e *ErrorManager) Execute(f func() error) {
	if e.Err != nil {
		return
	}

	e.Err = f()
}


func main() {
	em := ErrorManager{nil}
	em.Execute(func() error {
		return nil
	})
	em.Execute(func() error {
		return MailError{}
	})

	switch em.Err.(type) {
		case MailError:
			fmt.Println("Nem tudtam kézbesíteni a spamünket nagyuram :(")
		case DBError:
			fmt.Println("Nem érem el az adatbázist!")
		default:
			fmt.Println("Ok")
	}
}

A kód magyarázata:

Van egy structunk

ErrorManager

néven, aminek van egy

error

típusu mezője és egy metódusa amit

Execute

-nak hívunk. Az

Execute

metódusnak egyetlenegy paramétere van, mégpedig egy olyan függvény ami egy

error

típust ad vissza. Működése: az

Execute

szépen megnézi, hogy volt-e már hiba, ha volt nem csinál semmit, ha nem akkor lefuttatja a függvényt és feljegyzi az esetleges hibát, a struct megfelelő mezőjébe.

Mivel go-ban az

error

egy interface, ezért type assertation-t hajthatunk végre rajta. Ez alapján betudjuk azonosítani a hiba típusát (a kódban

MailError

,

DBError

) és ennek megfelelően cselekedhetünk. (https://golang.org/doc/effective_go.html#interface_conversions ; https://golang.org/ref/spec#Type_assertions)

Gyakorlatilag újra feltaláltad a kivételeket (megszakítod a blokk futását, de legalább messze kényelmetlenebb szintaxissal és úgy, hogy nem tudod garantálni, hogy ha valaki az em.Execute-ot lehagyva ír be oda bármit, az már ne fusson le), amiket a végén egyben kezelsz; viszont így legalább nyelvi támogatás híjján mindenhol neked kell alkalmazkodnod (random library nem ezt fogja használni, tehát szépen konvertálgatnod kell a hibákat).

BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)

"Gyakorlatilag újra feltaláltad a kivételeket..."
És? Mi baj van szegény kivételekkel?

"...úgy, hogy nem tudod garantálni, hogy ha valaki az em.Execute-ot lehagyva ír be oda bármit, az már ne fusson le..."
Miért kéne garantálnom?? Ha azt akarja valaki, hogy ne fusson le írja az em.Execute-ba.

"...(random library nem ezt fogja használni, tehát szépen konvertálgatnod kell a hibákat)..."
Nem mondtam, hogy ez egy szigorú dolog, hogy ezt csak így és így lehet használni: ki lehet egészíteni pl.: http://play.golang.org/p/aC1kVn5G3u

És? Mi baj van szegény kivételekkel?

Az egész szál onnan indult, hogy ki kell dobni a kivételeket, mert azok fúj, és mennyivel jobb, ha simán visszatérési értékben jelzünk hibát.

BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)

Ok sry, én nem olvastam el mindent :)

Mindent elolvasva, arra jutottam, hogy az alap defer-panic-recover lenne a jó megoldás:


        var err error = errors.New("Our Email infrastructure is broken")
	defer func() {
 		if r := recover(); r != nil { //lehet switch-elni r-re stb.
			fmt.Println("Huh we had an emergency, because:", r)
		}
	}()

	fmt.Println("[OK] db")
	fmt.Println("[BAD] mail") 
	panic(err)
	fmt.Println("[OK] everything") //ez _soha_ nem fog lefutni

Output:

[OK] db
[BAD] mail
Huh we had an emergency, because: Our Email infrastructure is broken

-> defer: egy függvényhez tartozó stack-ba rakja a kapott függvényt, majd ha a függvény végénél jár a futás, akkor lefuttatja őket egyesével (Pl.: http://play.golang.org/p/AZem_sh0XK)
-> panic: megszakítja a program futását és a függvényhez tartozó defer stack-hez ugrik, lefuttatja a benne lévő függvényeket, majd ezt szépen elismételgeti a hívási lánc elemein.
-> recover: megszakítja a panic futását

(http://blog.golang.org/defer-panic-and-recover)

>Az, hogy nem kivétel kezelés van, megfordítja a gondolkodást: nem azon kell gondolkodni, hogy "jé, itt dobhat valami kivételt, akkor engedjem tovább, vagy naplózzam és szálljak ki?", hanem rákényszerít arra, hogy MINDEN művelet adhat hibát, amit OTT HELYBEN le kell kezelni valami módon

Bámulatos innováció... volt >30 évvel ezelõtt...

Tényleg körbe-körbe jár az informatika?

"MINDEN művelet adhat hibát, amit OTT HELYBEN le kell kezelni valami módon."

Igen, de pont ettol valik konstans PEBKAC lehetosegge az egesz. Ha ugyanis a hiba csak ugy megtortenik, de a program nem all meg tole, akkor mindig lesznek olyan programozok, akik ignoraljak a hibat, es tovabb dolgoznak a hibas kimenettel. Ha mar kulon tool kell ahhoz, hogy kideritsd a kezeletlen hibak helyet, akkor az mar bonyolult. Ez egyebkent eleve compiler feladat lenne...
--
Blog | @hron84
Üzemeltető macik

Oké, csak minek? Van itt benn egy belső fejlesztésem, hibakezelés legtöbb helyen annyi, hogy a Command-ok törzsében van egy

try
{
// random muvelet
}
catch (Exception ex)
{
ErrorReportingWindow.ShowException(ex);
}

...ééééés ennyi. Nem érdekel, hogy hol, milyen hiba történt, hagyom megdögleni a műveletet, már csak azért is, mert történhet Exception egy helyi Argument(Null)Exception-tól egy távoli szerveren történő db/io exceptionig bármi. Legtöbb helyen érdemben csak annyit tudok kezdeni vele, hogy az usert étesítem, hogy gebasz van. Teljesen fölösleges 500 másik lépésben ellenőrizni a hibákat, mert úgy is csak annyi kell, hogy pusztuljon bele a művelet.

----------------
Lvl86 Troll, "hobbifejlesztő" - Think Wishfully™

Azért nem ilyen egyszerű a hibakezelés.
A hiba helyétől és fajtájától függően:

  • nem foglalkozunk a hibával, mert nincs hatással a program szempontjából semmire
  • megpróbáljuk javítani az előforduló hibát
  • újra végrehajtatjuk az adott műveletet, pl. újra megpróbálunk egy üzenetet elküldeni
  • "újraindítjuk" (újrainicializáljuk) az adott funkciót
  • feljebbi szint felé tovább adjuk a kezelést

Megnéztem a linkeket:

Az 1. link szerint azért nem jó a kivétel, mert inkonzisztens állapotú objektumokat hagy maga után. Ellenvetés: Erre találták ki a finally ágat. A finally ág végrehatódik a try blokk elhagyásakor, de NEM kapja el a kivételt.

A 2. link szerint azért nem jó a (Jáva) kivétel, mert rákényszeríti a programozót a hiba kezelésére. Idézet:


private String getContent(Path targetFile) {
   List<String> lines = Files.readAllLines(targetFile);//compile error: "Unhandled IOException"
   String content = Joiner.on("").join(lines);
   return content;
}

This code doesn’t compile, since Files.readAllLines() throws a checked exception. 
We are forced to handle this IOException. That’s annoying, but what can we do?

    Option A) Rethrow the exception
    Option B) Handle the exception

Végül a cikk arra jut, hogy exception helyett errorokat kell használni, idézet:


The Solution: Use Unchecked Exceptions And Wrap Checked Exceptions

Ez azonban baromság. A szerző nem jött rá, hogy az Option A. és Option B. helyett lehet csinálni a következőt:


private String getContent(Path targetFile) throws Exception 

Vagyis nem kell elkapni a kivételt, hanem feljebb lehet adni. Bár az igaz, hogy ez a módszer néha nem működik, ha előre meghatározott interfészt kell implementálni. Ez már egy Jáva tervezési hiba.

--
ulysses.co.hu

"Az 1. link szerint azért nem jó a kivétel, mert inkonzisztens állapotú objektumokat hagy maga után."
Egész pontosan nem azt írja, hogy hagy, hanem, hogy ez könnyen megeshet.
Ez hasonló eset, mint a Goto. Sokunk szerint ezek kerülendők, mert:

  • nehezen olvashatóvá teszi a kódot
  • nehéz refaktorálni
  • inkonzisztens állapotot hagyhat maga után
  • nehéz újrafelhasználni
  • nehéz kombinálni más dolgokkal (compose)

"Ez azonban baromság. A szerző nem jött rá, hogy az Option A. és Option B. helyett lehet csinálni a következőt:"
Ez az Option A, tovább dobod az exception-t.

"Ez az Option A, tovább dobod az exception-t."

Nononono. Ez igy ebben a formaban nem teljesen igaz. A tovabb dobott exception az ez:


try {
  giveMeSomeException()
} catch(Exception e) {
  throw APIAccessException(e);
}

A metodus szignaturaban jelzett exception pedig nem tovabb dobodik, nem all meg sehol kozben, hanem egyenesen a parent kod kivetelkezelojere ugrik.

Szerintem a ketto nem teljesen ugyanaz a technika, legalabbis en felreerthetonek tartanam mindkettore a "tovabb dobott exception" fogalmat hasznalni, hiszen a labda is csak akkor lesz tovabb dobott, ha en dobom tovabb. Ha atdobjak a fejem felett, akkor nem tovabb dobott labdarol beszelunk.
--
Blog | @hron84
Üzemeltető macik

Ezen a tovább dobotton, vagy csak simán dobotton el lehet polemizálni (szerintem is rossz a Rethrow kifejezés, talán jobb lett volna a (re)throw), a lényeg nem ezen van, hanem hogy a metódus dob-e "Checked Exception"-t, akkor jelezni kell a metódus szignatúrában, (akár dobták, avagy elkaptam és újradobok egy másikat,) vagy nem dob, akkor nem kell jelezni.

Option A: Checked exception-t dob (mindegy miért). Itt az a gond, ha változik az exception-ök köre, akkor az összes hívási listán végig kell vezetni az exception jelzést.

Option B: Elkapom az exception-t és kezelem. Itt az a probléma, hogy az adott helyen nem tudok ezzel mit kezdeni.

Option A' (by sz332): Elkapom a Checked exception-t, becsomagolom egy másik Checked exception-be. Ez annyival kicsit jobb, hogy ha változik az exception-ök köre, akkor többnyire használhatom a saját exception-ömet, nem kell a szignatúrákon módosítani. Viszont itt is megvan az a gond, ha teljesen más exception merül fel, vagy ha nincs semmilyen exception.

Azt azért hozzátenném, hogy az Option B eset (tehát, hogy exception jön, de azzal az adott helyen nem tudok mit kezdeni), az azt mutatja, hogy nem jól van szervezve a kódom.
Ha meg jól van szervezve a kódom és ahol felmerül az exception, ott tudok is vele mit kezdeni, az meg arra mutat, hogy felesleges az exception.

Nekem nincs bajom a checked exception-nal, mert nagyon jól mutatja, hogy itt bizony hiba lehet. Mondok egy példát: a GWT-ben a JSONParser
osztályban van egy parse() metódus, ami egy JSONValue-t ad vissza. Ha sikerül. Ha nem, akkor dob egy runtime exception-t. Ha nem figyel rá az ember, akkor simán elfelejti kezelni azt az esetet, amikor hiba van, és csak menet közben fog kiderülni, ha valami elszállt. Ellenben ha checked exception lenne, akkor ahogy irom a kódot, már szólt volna az eclipse, hogy figyelj, itt hiba lehet, kezeljed, vagy csinálj vele valamit.

A változó szignatúra az talán könyvtárak esetén lehet veszélyes, de igazából talán még ott sem. Ha van egy könyvtáram, ahol egy metódus X hibát dob, és bejön egy teljesen más, akkor azáltal, hogy egy throws NewException-t adok hozzá, jelzem a könyvtárat használóknak, hogy FIGYELJ, ITT VALAMI VÁLTOZOTT, kezelni kell a helyzetet!

Ezen szempontok szerint nem sok előnye van a Checked exception-nek a (Golang féle) több érték visszaadáshoz képest. A fordító ott is figyelmeztet, hogy csinálj valamit a hibával, és ha feljebb akarod kezelni, akkor ugyanúgy vissza adod a hibát is mindenhol. Hátránya viszont van, amint írtam is feljebb.

Inkabb azt soroljatok fel nekem hogy:

-Mit tud a Go amit a C++ nem?
-Mit tud a Go amit a Swift nem?

Elsosorban nyelvi szinten, most hagyjuk hogy mire milyen libet csinaltak eddig.

En meg majd eldontom, hogy ezek elonyok-e. Egy hatranya mar van nalam: Google. Nem lehet tudni mikor lesz deprecated es megy a "kozosseg kezebe". De ettol meg a nyelv maga lehet jo, csak mig a masik kettovel foglalkoztam, erdekel, mibol maradtam ki eddig, hogy a Goval nem (bar NagyZ azok kozt van, akire azert jo esellyel erdemes hallgatni, foleg ha Googler ismerosei modnjak hogy "nem jo", csak az is erdekelne, miert lehet).

Én nem hiszem, amíg használható GUI (MVVM; Windowsra, Androidra) és MVC keretrendszer nem lejenik meg hozzá.
Ebben az állapotában a 70-es évek színvonaslát idézi.

Fuszenecker Róbert

Jav: ahogy látom, MVC library van már, 0.12-es verzióval).

Ez a tévhite annak, aki nem tudja, hogy linux alatt _NEM_ freetype-pal hinteli a fontokat, továbbá nincs natív gtk (vagy qt) megjelenése sem.

Tehát nem teljesen igaz, hogy bárhogy kinézhet. Mint egy natív GTK-s app mondjuk egy alap Fedora vagy Ubuntu telepítésben, úgy sajnos nem :-(

Swing-nél soha nem volt cél az, hogy úgy nézzen ki az alkalmazás, mint az adott platform programja. Sőt, igazából osx-en kívül sehol nem láttam erre
még csak próbálkozást sem, hogy egységes kinézetű programok legyenek. Az általam windows-on használt programok lényegében teljesen máshogy néznek ki.

Java8 alatt is máshogy hinteli a fontokat? Mert nekem win7 alatt ilyen gondom nem volt...

> Swing-nél soha nem volt cél az, hogy úgy nézzen ki az alkalmazás, mint az adott platform programja
Akkor miért próbálják mégis utánozni, mind a három nagy platformon?

> Java8 alatt is máshogy hinteli a fontokat? Mert nekem win7 alatt ilyen gondom nem volt...
FYI Windows alatt nem is lesz a freetype-os hint miatt problémád... 8-cal még nem próbáltam, de ez valószínűleg azért is lehet, mert nem használok GUI-s java appot a mindennapjaimban.

De tegyük fel, hogy megjavulnak a fontok. Továbbra sem lesz natív gtk-s megjelenítés. (Kivéve ugye az említett swt esetét, egy ilyen szoftvert ismerek, de ott ugye a gtk, tehát a freetype rajzolja a fontokat is)

Azt nem írtam, hogy natív. A "szép GUI"-ra reagáltam. Ha egy gtk lehet szép, akkor egy swing is :) Mellesleg az Eclipse swt-s. Nekem sehol se tűnt natívnak. Szépnek meg pláne nem :) A natív kinézetet osx-en és windowson valszeg (gondolom én) azért próbálják utánozni, hogy ne linux desktop érzés legyen, ahol minden másképp néz ki :) De pl az IntelliJ IDEA swing, és szerintem nem csúnya :)

OK, értelek - a félreértést akkor az okozza, hogy nekem az egységesség hiányzik. :-) Mármint szeretem, hogy a Qt-s skype, a XUL-os firefox, az SWT-s eclipse képesek ugyanúgy kinézni csak a swinges appok képtelenek erre.

És ami viszont tényleg nem szép, azok a fontok. És linux alatt az intellij és a netbeans is így jön sajnos, egyedül az eclipse (a gtk-s/freetypeos hinter miatt) képes arra, hogy a fontokat szépen megjelenítse.

Pont erre gondoltam, hogy ha engem is magával ragadna a csillióKás felbontások világa, valószínűleg már nem lenne ezzel probléma, a kérdés csak az, hogy a Jávás csodaszoftverek lekövetik-e a trendet, és használhatóak-e ilyen felbontás mellett, vagy egyszerűen csak kifeszítenek egy pixelt négyre. Vagy kilencre.

Egy sörből nem lehet megélni. :)

A mai felhasználó (megrendelő) böngésző alapú, modern, reszponzív, mobil verzióval rendelkező, vállalati/intézményi arculathoz illeszkedő, többnyelvű, helyzetérzékeny súgóval ellátott, gyengén látók számára is használható, gyorsbillentyűzött, magyar betürendű és formázottságú, szerepkör és engedély alapú hozzáféréssel szabályozott, többfajta és többfaktoros belépési mechanizmusokat, sso-t lehetővé tevő, ügyfélkapu, faszbúk, titi-twitter, gugli+ kapcsolattal rendelkező, q32feafsd, asd faewr sagf, wergerg, aerggcf, asrtqre, 4t5gefvd, sdffg45t, 4gasd,gwerg rwegtrgfvmcv, #&>@#&@!!!?.- felhasználói felületet szeretne. :)

Mocskosul szét van offolva ez a topic, engem se igazán érdekel a go, de azért drukkolok neki! :D