Hibakezelés

Sziasztok!

Másik topicban 

https://hup.hu/comment/3241234#comment-3241234

előkerült a normális hibakezelés kérdése és mivel ez egy olyan téma amit több különböző módon lehet jól és rosszul, kényelmesen és kényelmetlenül csinálni, megér akár egy dedikált fórum témát is.

Plusz témaindítónak pár ötletet feldobok:
- Hibakód?
- Exception?
- Hibaobjektum?
- Hibakezelő függvényreferencia?

- Hogyan kezeljük a hibát több alakalmazás rétegen keresztül?
- Hogyan kezeljük a hibákat rendszereken átívelően?

- Mitől más - elsősorban kódolási, kényelmi, stílusbeli szempontból de technikai és teljesítmény oldalról is - ha egy függvény hibát ad vissza illetve ha exception-t dob?

- Checked exception témakör. Lehet, hogy ott ment ez félre, hogy rosszul osztály
ozzuk a hibákat? Most így hirtelen az alábbiak jutnak eszembe:
 - Üzleti logikai esetek reprezentálása exceptionnel
 - Tranziens hibák amik esetleges újrafuttatással javíthatók tranzakción belül
 - Tranziens hibák amelyek új tranzakcióban javíthatók
 - Nem javítható hibák amiket csak logolunk és megy tovább az élet
 - Nem javítható hibák amiktől megáll a rendszer


Küldjetek példákat opensource projektekből jól megvalósított hibakezelésre! (bármelyik fajta jöhet)
 

Hozzászólások

Kövezzetek meg, de én a hibák kezelését sosem különítettem el gondolatban az üzleti logikától, mert a legtöbb hiba az egy pont olyan feltétel valahol, mint az összes többi. Például user fel akar tölteni egy egy gigás .jpg állományt, ez ugye elméletileg nem hiba, de gyakorlatilag egy nagy lóhimbilimbit fogom neki megengedni. Ha a processzem le akar írni egy MB -ot, de betelt a lemez, akkor jön a hiba, de ez igazából olyan, mint amikor én mondom a felhasználónak, hogy hát ilyet most nem tehetsz az egy gigás képeddel, csak ezt az oprendszer közli velem.

Ergo én a hibakezelést mindig úgy végzem, ahogy a körülötte lévő üzleti logika megkívánja. Volt olyan projektem, ahol személy szerint borsózott a hátam, mert tele volt olyanokkal a kód, hogy if (valami != null) valami.color = color.red; meg hasonló dolgok. Ez egy rettentő laza kód, hiszen szarik bele, ha valami nincs a helyén, olyankor egyszerűen nem csinál semmit, de ez megengedhető? Van ahol igen, és van ahol nem. Vagyis itt a hiba (a null pointer) kezelve volt, már ha a szőnyeg alá seprést kezelésnek tekintjük.

Általános igazságot, hogy mi a legjobb kezelése a nem várt dolgoknak nem fogunk tudni mondani, főleg azért, mert a hibakezelés pont az, hogy a dolgok bekövetkezésére felkészülünk. Kivéve talán a teljesen generális Exception -t, de az meg marhára nem vezet sokra, ha olyan mély rétegből érkezik valami halálsikoly, hogy már esélyünk sincs értelmesen reagálni rá. Ha viszont készülünk a dolgokra, akár helyben egy Exception kezeléssel, akár egy file write függvény hívásakor leellenőrizve, hogy hány byteot tudott kiírni, akár egy pointer értékének ellenőrzésével, vagy bash -ben az stderr tartalmát megnézve, akkor máris tudunk vele valamit kezdeni. De hogy mit, azt az üzleti logika adta követelmények szablyák meg a számunkra, ergo a hiba kezelése az üzleti logika része.

Vagyis, ahogy nem tudunk legjobb módszert mondani az összes üzleti logika leprogramozására, úgy nem lehet legjobb módszert mondani a hiba / kivétel / ritkán előforduló esetek leprogramozására sem.

Form follows function.

Szoktam csinálni az

if (ptr) *ptr = value;

típusú megoldást, de ez nem laza kezelés, hanem annak megengedése, hogy ne legyen kötelező megadni egy függvénynek a változó címét, amelyben ezt az outputot visszaadja, hanem lehet NULL-t is adni, ha ez a kimenet nem érdekel minket.

Egyébként szerintem elírtad, így kellene:

if (valami != NULL) valami->color = color.red;

Én egyébként hibakódot szoktam visszaadni C-ben, a tényleges outputot pedig a hívó helyen megadott változó vagy struktúra, esetleg tömb címének átadásával, a függvényen belül a cím dereferálásával.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Nem, dehogy! Kétségkívül úgy volna elegáns, hogy ha lenne egy általános, hibaleíró struktúra, bár akkor már lehetne az egy globális változó, felesleges átadni a címét. Abban el lehetne mondani a hiba keletkezésének helyét, okát, mindenét. De nem ezt teszem.

YleGreg hozzászólására válaszoltam, mert azt írta példaként, hogy a NULL pointer vizsgálatával megoldjuk, hogy NULL esetén nem adunk outputot, az a dolgok sárral való bekenése, de szerintem nem, mert van egy rakás függvényem, amelyik olyan, hogy több kimenete is van, de nem mindig érdekel az összes outputja. Akkor pedig ami nem érdekel, oda NULL címet adok át, a függvény pedig azt nem dereferálja.

Amúgy jellemzően int8_t hibakódot adnak vissza a függvényeim, illetve logolom, ha valami baj van. Muszáj életben maradnia a programnak, nincs hova kilépni egy mikrokontroller firmware-éből, tehát értelmes hibakezelést kell csinálnom.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Az nem opcio, hogy az adott programnyelv adott keretrendszerenek az ajanlasat koveted? Lehetoleg a celnak megfeleloen.

pl.:

Random, egyszerhasznalatos scriptben nincs, ha nem kritikus (torolhet valamit).

C++, Java program: exception

Go: asszem helyben lekezeled, de nem ismerem jol - azt tudom, hogy mast valasztottak

C low level dolgok: hibakod

Node JS Express: parameterszam+hibaobjektum (ha jol emlekszem)

Egeszsegugy, urtechnika, vonat fekrendszer: eloirasoknak megfeleloen

A strange game. The only winning move is not to play. How about a nice game of chess?

Ha mindent jól csinálunk akkor semmi sem lesz rossz :)

Az ember követi az adott környezet konvencióit, ez természetes, különösen meglévő projektnél, ráadásául minden nyelv esetében van amit könnyebb, van amit nehezebb megvalósítani. Éppen ezért hoztam létre külön topicot a témának, hogy hátha lesznek olyanok akik konkrét jó példákat tudnak adni ennek a nem triviális feladatnak a megoldására illetve egy példáról el is mondják, hogy mi az előnye és a hátránya.

 

Az alapötlet, hogy használd a framework / környezet megoldását, az jó. 
 

De ez nem ennyire fekete / fehér pl. C++ / Java-ban sem:
- Teljesítménykritikus kódban C++-ban sem szokás exceptiont használni. 
- Java-ban is vannak olyan rendszerek, ahol "Error as Value" megközelítést használnak.

Egyreszt csatlakozom az elottem szolokhoz. Masreszt meg erdemes lehet megnezni a hiba kivaltasanak jelleget es aszerint cselekedni. Pl input sanitizing, de a coverage is ide tartozhat (pl legyen minden if/elseif-hez else ag, switch/case melle default, stb). Es akkor talan konnyebb kitalalni hogy mit tennenk akkor amikor azt hittuk hogy hogy pedig dehogy.

Beagyazottabb/rendszerkozelibb/idokritikusabb esetekben meg talan a data flow iranyabol foghato meg a dolog (mit csinalunk ha jon adat de nem tudjuk letarolni mert nincs hely, mit csinalunk ha kuldenenk adatot de foglalt a fizikai illeszo ami kuldene, mit csinalunk ha rafutas van, mikor szabad blokkolni es mikor nem, stb). 

Én szeretem az exceptiönt, ha van. Szerintem az a legfejlettebb és legjobb megoldás.

A Rust és egyebek hiba-érték felfogása szerintem rossz, az egyik Rustos topikban leírtam, hogy miért. Persze, azzal is meg lehet csinálni amit kell, csak nem nevezném erősségnek, hanem inkább egy tolerálandó tulajdonságnak. Szerintem aki nem szereti az nem érti. Illetve ilyen idealista, és nem látja be, hogy a hiba értékként éppen ugyanannyira rossz, csak kézzel kell megcsinálni azt, amit exceptiön esetén a fordító megcsinál.

A C esetén megbocsátható, hogy nincs Exception, érthető, akkor még más világ volt. De a magyarázatokat, amikkel jönnek az új nyelveknél, hogy miért nem szeretik az exceptiönt, azokat nem tudom elfogadni.

Egyetlen egy magyarázatot mégis el tudok fogadni: az új nyelveknek együtt kell tudni működni C-vel, és esetleg emiatt nem tudnak bele exceptiönt tenni, mert a stack C-s részével nem tudna jól együtt dolgozni. Ezt nem tudom pontosan, hogy mi az igazság ezzel. A WASM-ban például nincsen exceptiön, de ha WASM-ból belehívsz JS-be, és ott jön exceptiön, akkor a stacktrace WASM részét is átugorja (kipróbáltam). Szóval ott például együtt működik a kettő valahogy.

Nagyrészt érdemes követni az adott nyelv/framework ajánlásait. Persze ezek lehetnek korlátozottak, problémásak.

Pl. C-ben visszatérési érték a szokásos. Ez a C low-level szemléletéhez jól illeszkedik, az exception kezelés ugyanis egy csomó egyéb dolgot generál a compiler outputba. A probléma vele, hogyha a programozó hanyagságból nem vizsgálja a visszatérési értéket, ezzel implicit feltételezi, hogy nem is történt hiba. Főleg olyan kódokban fordul elő, amit csak gyorsan megírtak, aztán véletlenül "production" kódként fut 20 éve. Ha hibakezelés nélkül rákerül a vezérlés a következő sorokra, úgy, hogy az előző hívás igazából nem csinálta meg teljesen, amit vártunk tőle, akkor valahol később fog robbani a bomba, és tipikusan jóval nehezebb a hiba okát megtalálni.

A Go-ban kb. félúton vagyunk, ott visszajön a normál visszatérési érték, és a szokás, hogy mellette egy külön változóban opcionálisan a hibakód. Ez jobban rávezet arra, hogy észleld hogy hiba van. Viszont sok boilerplate kódot eredményez, mindig kell egy ág arra, hogy mi van, ha hiba volt. Ahol legtöbbször csak szintén visszatérsz a függvényedből egy hibakóddal. És kellő komplexitás után eljutsz oda, hogy a legtöbb függvény hív olyan függvényt, ami adhat vissza hibát, tehát a te függvényednek is kell tudnia hibát visszaadni.

A magasabb szintű nyelvekben a szokásos az exception. Ez sok előnnyel jár, a legfontosabb az, hogy nem marad a hiba detektálatlan, azonnal megszakad azon a ponton a futás, nem megy tovább a vezérlés a következő sorra, ami dependálna az előző sikerességére. További előny, hogy könnyebb utólag megtalálni a logokból, hogy hol mi történt root cause-ként, főleg, hogy szinte mindig ott van a stack trace információ is. Még további best practice, hogy az exception-öket érdemes láncba kötni, ha valahol elkapod, de nem tudsz vele mit kezdeni, akkor továbbdobsz egy új exception-t, aminek beállítod kiváltó okként az elkapott exception-t, ez a logokban később szintén visszakövethető. Ezzel az absztrakciós szintet tudod jobban korádban tartani, pl. ha az adat réteged DataException-t dob, attól még belül lehet NetworkException vagy FileException a belső kiváltó ok, attól függően, hogy hálózati vagy helyi adatkezelő implementáció van mögötte, de kívülről mindegyik DataException-be van csomagolva.

Az exception kezelésnél a legizgalmasabb kérdés, hogy hol mikor érdemes kezelni catch blokkal. A Java-ban ebből a szempontból van egy érdekes konstrukció, a checked exception, ami a függvény szignatúrájában jelzi, hogy valamilyen exception "várható". És ezt muszáj a hívás helye környékén kezelned (vagy úgy ahogy van, továbbdobni a függvényedből, de akkor a te függvényed szignatúrája is bővül ezzel). Erről a fajta megoldásról manapság már többnyire azt tartják, hogy nem biztos, hogy a legjobb döntés volt.

Az én tapasztalatom szerint a legjobb, ha az exception-ök az igazán kivételes esetekre vannak fenntartva, amivel a hívás helyéhez közel nem tud mit csinálni a program, pl. ha betelt a diszk, vagy ilyesmi. És ebből következik, hogy ezeket csak a hívási lánc gyökeréhez közel van értelme elkapni és loggolni, a usernek meg valami hibaüzenetet megjeleníteni, hogy nem sikerült az aktuális művelet. Hogy mi az aktuális művelet, az a futási környezettől függ. Pl. webszerver esetén az adott webes kérés lesz sikertelen, de a webszerver megy tovább. Parancssori cli programnál talán célszerűbb az egész programból kilépni. Persze vannak kivételek a kivételkezelésben :D, ritkábban elképzelhető olyan eset, hogy a hívás helyéhez közel kapod el, ennek akkor van értelme, ha tényleges tudod úgy lekezelni a hibát, hogy az adott művelet utána be tudjon fejeződni. De ez egyben azt jelenti, hogy valamennyire már felkészültél az alternatív ágra.

És ekkor felmerül a kérdés, hogy nem lett volna-e jobb úgy felépíteni a kódodat, hogy exception mentesen, elágazással menjen az egyik vagy a másik irányba. Tipikus példa, hogy egy key-value adatszerkezetből lekérünk egy key-hez tartozó értéket. Vajon kell-e dobódnia exception-nek, ha nincs a key a tárolóban? A helyes válasz, hogy attól függ :) Ha az egy normál működése lehet a programnak, hogy a keresett key nincs benne a tárolóban, akkor nem kell exception, akkor csak simán az az ág fut le, ami úgy dolgozik tovább, hogy mondjuk ő rakja bele a key-t. Ha az a normál működése a programnak, hogy feltételezzük, hogy akkor konzisztens minden az adott ponton, ha a key-hez van érték a tárolóban, akkor meg igenis dobódjon exception, hiszen egy kivételes helyzetben vagyunk, már rég elromlott valami.

További kiegészítés az exception-ök előnyéhez, az automatizált erőforrás felszabadítás. Ami C++ -ban RAII, C#-ban using és Disposable, Java-ban a try és Closeable, Python-ban a with, az mind azt segíti elő nagyon természetes módon, hogy akármilyen módon lép ki a vezérlés egy blokkból, a happy path-on vagy valami exception-nel, a blokkban megnyitott erőforrások lezárása minden esetben megtörténik.

Szépen összefoglaltad, Riszpect ;-).

Egyetlen kiegészítésem lenne ehhez:

ekkor felmerül a kérdés, hogy nem lett volna-e jobb úgy felépíteni a kódodat, hogy exception mentesen, elágazással menjen az egyik vagy a másik irányba.

Ez a döntés nemcsak adott helyzet logikájától függhet, hanem az adott kivételes helyzet mennyire gyakran kerül elő, milyen következménye van. Ha soha vagy nagyon ritkán (szubjektív), és nem okoz különösebb gondot, akkor valószínűleg felesleges úgy felépíteni, hogy ne legyen kivétel, főleg ha drága (szubjektív) az előzetes vizsgálat (pl. van-e elég szabad hely a lemezen, mert az adott lemez valahol máshol van és közvetlenül elérhetetlen a futási kontextusból). De ha rendszeresen előfordul a hiba, akkor célszerű lehet már a folyamat kezdetén ellenőrizni a feltételeket, hogy adott esetben drága folyamat el se induljon azért, hogy a végén jól megszakadjon és mindent vissza kelljen csinálni, aminek szintén lehet kockázata. Például egy függvény, ami ellenőrzi a megfelelő helyen, van-e elég tárterület, ha nincs, akkor el sem indul a folyamat (felhasználótól nem kér be X darab adatot stb), hanem jelzi, hogy gond lesz.

Egyszerű példa erre egy szoftver telepítése, ami előre ellenőrzi, hogy fog-e tudni települni, van-e elég hely: sokkal egyszerűbb és célszerűbb, ha nem indul a telepítés, ha jó eséllyel nincs elég hely, minthogy futna ki tudja mennyi ideig (akár órákig, mert le is kell töltenie magát), a végén csak romok maradnak utána, mikor kiderül, hogy letölteni még csak-csak sikerült, de települni már nem fog. Rosszabb esetben még az OS is összeomlik. Nyilván más a helyzet egy sok GB-os szoftvernél és más a helyzet egy néhány KB-os esetén.

en szeretem a C megoldasat, hogy altalaban -1et vagy NULL-t adnak vissza hiba eseten a posix fuggvenyek. a cleanup tenyleg maceras tud lenni, foleg ha sok close/free kell egy belso hiba kezelesenel, de ez inkabb a nyelv korlata, mintsem az exception hianya.

pythonban viszont az agyamra megy, hogy mindenre exceptiont dobal, kicsit tultoltak szerintem. vagy pl itt van ez a gyakori struktura:

try:
     value=my_dict[key]
     process(value)   # valamit csinalunk a value ertekevel
except: pass

itt ugye az az elvart mukodes, hogy csak akkor csinalunk valamit ha a key-hez tartozik value. de ha tartozik value, de a feldolgozasakor lesz valami hiba, akkor is megeszi csendben a hibat, es ugy fog viselkedni, mintha nem szerepelne a dict-ben. nyilvan ilyenkor except Keyerror vagy ilyesmit kene irni, de az esetek 99%-ban nem szoktak...

try:
     value=my_dict[key]
     process(value)   # valamit csinalunk a value ertekevel
except: pass

Hát igen, ez egy antipattern. Ha az egy valid ág, hogy a keresett key nincs benne a dictionary-ben, akkor ahogy említettem, az exception mentes keresést kell használni, mondjuk a get() metódust. Ha meg elvárt, hogy mindig ott legyen a key, akkor lehet az exception dobó indexelő operátort használni, viszont akkor try-except nélkül, hogy továbbdobódjon az exception.

az exception mentes keresést kell használni, mondjuk a get() metódust

^ ez. Többnyire van erre megoldás, ha a kulcsnak benne kell lennie, mert kell az értéke, akkor [key]; ha meg jó a None érték, akkor .get(key), ha ilyenkor default érték kell, akkor meg .get(key, 0). Nem hosszabb vagy rosszabb.

Jobban szeretem az in-t ilyenkor. A kettos lookup idejet le kellene merni, nem vagyok biztos benne, hogy annyival rosszabb, mint a kivetel.

A get-et irtak mar, ami None-t (vagy a parameterul kapott erteket) adja, ha nincs benne, de a defaultdict-et valamiert nem:

Ezzel a [] ugy fog viselkedni, mint a get, defaultban None-nak veszi a hianyzo erteket. Ha mondjuk stringek elofordulasat szamolod (pl. LLM tanitasnal az adat elokeszitesenel), akkor a defaultdict konstruktornak int-et is adhatsz parameterkent, akkor 0 lesz ez a default ertek. Szerintem eleg hasznos tud lenni. Ja, ez a parameter (default_factory) sajat tipussal is gyonyoruen mukodik, meg pl. list is megy vele.

Doksibol par pelda:

s = 'mississippi'
d = defaultdict(int)
for k in s:
    d[k] += 1

sorted(d.items())
# [('i', 4), ('m', 1), ('p', 2), ('s', 4)] lesz belole

Vagy a masik:

s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
d = defaultdict(list)
for k, v in s:
    d[k].append(v)

sorted(d.items())
# [('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

A strange game. The only winning move is not to play. How about a nice game of chess?

ez a defaultdict nagyon tetszik. miota van ez pythonban? sose halllottam rola, nem is lattam hasznalni meg sehol.

ez valami uj cucc gondolom? bar rakeresve talaltam mar 3.9-es referenciakat is es 3 eves posztokat.

Python 3.8.10 (default, Mar 18 2025, 20:04:55)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> x=defaultdict(int)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
NameError: name 'defaultdict' is not defined

ja ez nem beepitett tipus :(

>>> from collections import defaultdict
>>> x=defaultdict(int)
>>> x[3]
0
>>> x[111]
0
>>> x.keys()
dict_keys([3, 111])

es kar, hogy olvasasnal mar lefixalja a kulcsot

Gyors keresessel 2.7-ben es 3.0-ban megtalaltam, a 2.6-ban nem. Szoval olyan 2008 korulre tippelnem.

Alap telepites resze, szoval nem kell telepiteni semmit pip-pel, ilyen szempontbol beepitett, de nem szemeteli tele ezzel sem a namespace-t ha nem hasznalod, szoval importalni azert kell. Ugyanugy, ahogy mondjuk a math is alapbol fent van, de be kell huznod hasznalat elott (legalabb azt, amit hasznalnal belole). Opcionalisan a pythonrc segit, ha sokszor hasznalnad interaktiv modban - kodban meg mindegy, az IDE megoldja.

Ha nem jo, hogy lefixalja, csinalhatsz szerintem sajatot, a megfelelo dunder method-ot kell hasznalnod (getitem, missing). De ennyire nem merultem el a lelkivilagaban.

A deque meg az OrderedDict nekem eddig tobbszor kellett.

Neked meg a Counter lehet jo, az pont erre van - hashable objektumok darabszamara.

A strange game. The only winning move is not to play. How about a nice game of chess?

osszedobtam egy kis benchmarkot, eleg meglepo eredmennyel:

Python 3.10.12
input length: 24544558
3.5733723640441895 249171 249171 set
6.6800217628479 249171 24544558 except
7.650585889816284 249171 24544558 if+in
6.381649494171143 249171 24544558 dict.get
6.4901769161224365 249171 24544558 defaultdict

ez egy mar elore tokenizalt 100 megas txt-t iteral vegig, 24.5M tokent szamol meg, hogy melyikbol mennyi van. 
elso szam az ido, masodik a kulcsok szama a dict-ben, 3. szam a szo-szamlalok osszege (sum(dict.values()), ennek egyezni kene az input meretevel ugye.

az elso csak dict[word]=1 igy az nem ad helyes eredmenyt, csak azert raktam be hogy lassuk maga az iteralas es dict iras mennyi ido.

ahogy az varhato volt, az if x in dict: dict[x]+=1 else: dict[x]=1 felallas a leglassabb, de az meglepett hogy meg a defaultdict is gyorsabb a (regen legalabbis) ajanlott try-except konstrukcional, de a dict[x]=dict.get(x,0)+1 nyerte a futamot.

update: megneztem 3.11-el is ami ugye ismert hogy sokkal gyorsabb, hat itt a sorrend is borult, es a try-except nyert:

3.1912825107574463 249171 249171 set
5.645915508270264 249171 24544558 except
6.72413444519043 249171 24544558 if+in
5.777003526687622 249171 24544558 dict.get
5.784819841384888 249171 24544558 defaultdict

Én dict.get párti voltam, és maradok is.

Két okból: Ha jó a default érték, akkor egyszerű a második paraméter miatt. Ha meg nem jó, akkor if val == None: után helyben kezelem le, hogy mi van akkor, ha semmi nincs, ez nekem szimpibb, mint elvinni jó pár sorral arrébb a vezérlést try-except -el. Kivétel persze, ha ez olyan probléma, amit nem itt helyben oldok meg, vagy lépek át.

Én szeretem egy helyre írni az adott dolgot végző kódot, így az adott blokk végén már tudom, hogy ki van simítva minden, le van kezelve az összes lehetőség. A try-except ezt a helyben kezelést egy kicsit széthúzza, legalábbis gondolatban, mert egy None az én fejemben nem hiba, ezért simán if -el kezelem le, míg a try-except egy vészcsengőt szólaltat meg a fejemben, hogy na itt valami nagy galiba okozódhat, figyeljünk rá.

Form follows function.

Ha sokszor futtatod, valtozik a sorrend? Lehet, hogy nincs akkora kulonbseg.

A Counter-t meg megirnad hozza? Kivancsi vagyok, mennyit optimalizaltak rajta - pont erre valo, amire hasznalod, ha nagyobb resze low-level C, verheti az osszes tobbit.

A strange game. The only winning move is not to play. How about a nice game of chess?

a meresnel eleve 5x futtatja le egymas utan, de probaltam manualisan is tobbszor, mindig ua, max 0.01 elteresek vannak

https://pastebin.com/iaxiEkVZ

beleirtam a countert, es ezt is:         d[word]=d[word]+1 if word in d else 1

root@ddr5:~/dict-teszt# python3.11 1.py
input length: 24544558
3.024920701980591 249171 249171 set
5.498338460922241 249171 24544558 except
6.482637643814087 249171 24544558 if+in
5.701817512512207 249171 24544558 dict.get
5.685314416885376 249171 24544558 defaultdict
6.589926242828369 249171 24544558 value-if
4.3745787143707275 249171 24544558 Counter

root@ddr5:~/dict-teszt# python3.10 1.py
input length: 24544558
3.6069064140319824 249171 249171 set
6.713489294052124 249171 24544558 except
7.723560571670532 249171 24544558 if+in
6.448418617248535 249171 24544558 dict.get
6.615523099899292 249171 24544558 defaultdict
7.276813983917236 249171 24544558 value-if
4.320647954940796 249171 24544558 Counter

upsz most latom a value-if verzional benne felejtodott az elozobol a defaultdict, javitva {}-ra picit jobb de nem sokkal:

root@ddr5:~/dict-teszt# python3.11 1.py
input length: 24544558
3.0277678966522217 249171 249171 set
5.522897481918335 249171 24544558 except
6.49593186378479 249171 24544558 if+in
5.7229509353637695 249171 24544558 dict.get
5.72023868560791 249171 24544558 defaultdict
6.405221223831177 249171 24544558 value-if
4.399137735366821 249171 24544558 Counter

Koszi!

Akkor a Counteren optimalizaltak valamennyit. Viszont osszessegeben azt hiszem, hogy nem ezen mulik a dolog, annyira nagy elteres nincs az except meg a get/defaultdict kozott.

Egyebkent arra szamitottam, hogy az except ugye uj objektum letrehozasat is jelenti (mert benne van a hianyzo key), es ezert lassabb lesz. Az in-es megoldasnal meg arra, hogy par kulcsnyit cache-el a kevesbe kioptimalizalt kodok miatt.

A strange game. The only winning move is not to play. How about a nice game of chess?

Ilyet én is csináltam már komplexebb cuccokban, nálam a lényeg ilyenkor az, hogy ilyenkor az a cél, hogy az összes lehetséges key-re megfussunk, és legfeljebb adott értékre nem csinálunk semmit, de amikor van 8512 darab lehetséges key, és ebből csak 12-nél jön a hiba, akor nagyobb benefit, hogy a 8500-at megcsinálja jól, a maradék 12-t meg lelogolja. Nem mindig egyszerű ugyanis egy folytatólagos logikát belerakni, vagy csekkolni, hogy ugyan megfutottunk-e már. Annyiban szokott ez nálam más lenni, hogy az except -nél előszedem az exceptiont, jól lelogolom a key-t meg a hibát és megy a pass tovább.

Blog | @hron84

valahol egy üzemeltetőmaci most mérgesen toppant a lábával 

via @snq-

Javaban ezért találták ki az Optional<T> visszatérési értéket, ahol fluent módon lehet kezelni mindkét visszatérési értéket. Van olyan elmélet, hogy minden metódus visszatérési értéke legyen Optional <T>, List<T>, vagy Map<T>, az utóbbiaknál empty, ha null lenne. Így az exception-t fenn lehet tartani a nem várt hibák kezelésére és nem kell vizsgálni null értékre. 

Kotlin is hasonló, bár még szofisztikáltabb.

Szerintem ez egy csomó könyvben végig van szálazva, biztos, hogy érdemes ezt kinyitni így? :)

Röviden:

- a hívott cucc dobjon exception vagy bármi hasonlót, ha a hibát nem az input okozta, hanem az üzemszerűtől eltérő állapota a rendszernek, ami a cucc mögött van, majd a cucc hívója eldönti, hogy mit kezd ezzel az információval,
- adjon vissza értelmes hibaüzenetet és hibakódot, adatokkal, ha minden üzemszerűen működik, de a hibát az input okozta, például nem létező id lekérdezése, a cucc hívója eldönti, hogy mit kezd ezzel az információval.

Pár marginális kivételtől eltekintve ez az, ami egy értelmesen megírt rendszerre igaz.

Ha ez olyan egyszerű lenne, akkor nem mennének késhegyre menő viták egyik-másik topicban ennek apropóján. :)

Ha tudsz valami jó könyvet a témában ami a beágyazott rendszerektől a nagyvállalati elosztott környezetekig lefedi a hibakezelést és le van benne írva írva, hogy az egyes szinteken mit nyersz és mit veszítesz az egyes megoldásokkal, szívesen veszem.

Azt nem egészen értem amit írsz. Az első esetben nem kell információ meg hibakód? A második esetben csak feltételezem, hogy ez is valami exception lesz amibe a hibakód meg az üzenet utazik.

Ha ez olyan egyszerű lenne, akkor nem mennének késhegyre menő viták egyik-másik topicban ennek apropóján. :)

Viták mindig lesznek.

Ha tudsz valami jó könyvet a témában ami a beágyazott rendszerektől a nagyvállalati elosztott környezetekig lefedi a hibakezelést és le van benne írva írva, hogy az egyes szinteken mit nyersz és mit veszítesz az egyes megoldásokkal, szívesen veszem.

Ilyen könyv nem lesz, mert egy beágyazott rendszer másképp működik, mint egy elosztott nagyvállalati rendszer... vannak viszont olyanok, amelyek remek összefoglalót adnak egyes területekhez és az alapja ugyanaz lesz: ami nem üzemszerű hiba, az exception (vagy bármi ehhez hasonló), ami üzemszerű hiba, az meg a válaszban benne lesz.

Van egy régebbi jó könyv a témában (több mint 10 éve olvastam, lehet, hogy van belőle frissebb változat): https://www.amazon.com/Advanced-Exception-Handling-Techniques-Computer/dp/3540374434

Illetve a listámon rajta van egy friss, amit még nem olvastam: https://www.amazon.com/Exception-Handling-Fundamentals-Programming-SpringerBriefs/dp/3031506804

Illetve van a "Clean Code: A Handbook of Agile Software Craftsmanship", ebben is van sok megfontolandó bekezdés erről a témáról: https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0135398576

 

Az első esetben nem kell információ meg hibakód?

Az exception ezt mind tartalmazza általában.

A második esetben csak feltételezem, hogy ez is valami exception lesz amibe a hibakód meg az üzenet utazik.

Nem, a második esetben a visszaadott struktúra tartalmazza mindezt, mert felkészültél az összes olyan hibára, amit az input okozni tud üzemszerűen.

Nagyjából tényleg az van, hogy ha nem üzemszerű a hiba, akkor exception, ha üzemszerű, akkor a visszaadott érték/struktúra tartalmazza a hibát.

Szerintem ez egy csomó könyvben végig van szálazva, biztos, hogy érdemes ezt kinyitni így? :)

Ezen én is gondolkoztam, hogy mennyi értelme van, mire megírtam a korábbi hosszabb hozzászólásomat :) Ráadásul nemcsak a könyvekben van ez meg, hanem az ilyen a lexikális tudás és gyakorlati tapasztalatokon alapuló best practice tudásmegosztásban az AI válaszok is egészen jó minőségűek tudnak lenni.

Ettől függetlenül szerintem néha nem ártanak a szakmai témák :D

A szoftver tervezés módszertana egyáltalán nem lezárt tudomány. Ez nem természeti törvény, hogy valami így vagy úgy van és vitának helye nincsen. Már a gondolat is rossz, hogy azért mert könyveket írtak valamiről, azért ne lenne érdemes beszélni róla.

Például a Franko által citált "Clean Code" könyvnek rengeteg komolyan vehető kritikája van. Egy konkrét példa, ami nagyon elgondolkodtató, az ez:  "Clean" Code, Horrible Performance Molly Rocket Casey Muratori 22perc, safe for work: https://www.youtube.com/watch?v=tD5NrevFtbU

Én is arra jutottam privát megfontolásból, hogy többségében nincs igaza Uncle Bobnak. A vélt esztétikumból nem érdemes kiindulni. Hogy mégis melyik megoldást lesz a jó? Ha nem mérjük a hatékonyságot, akkor sajnos be lehet dőlni jól hangzó lózungoknak, amik a valóságtól messze vannak. Például a Clean Code tipikus példái felesleges elbonyolítások, amik a futásidőt, a debuggolhatóságot és az átláthatóságot is rontják. Pont fordítva van, mint ahogy állítja a könyv, sokkal nehezebben átlátható egy clean clode szellemben írt alkalmazás, mint valami ami az egyszerűségre törekszik. És ezt a Clean Code iskolapéldái is már bizonyítják!

Egy példa: Ha írsz egy 5 oldalas switch case-t, azt bárki át fogja látni pár percen belül. Mert ott vannak az orra előtt a lehetőségek, ott van, hogy válaszként melyikre mit fog reagálni a program. Csak a görgetést kell használni az egéren, hogy mindent megtalálj, amire szükséged van. A Clean Code-osok egy 5 oldalas switch case-től agyhúgykövet kapnak, és szétbontják nagyjából 10 osztályra virtuális metódusokkal. Na, ezt a 10 osztályt próbáld meg átlátni! Vagy debuggolni! Vagy statikusan analizálni! Egy nagyságrenddel nagyobb a komplexitása. Persze a Clean Code-osok tagadni fogják, mert ez is ilyen vallás-szerű mint a Rust.

Hasonlóan fogom a fejem azon, hogy elvi megfontolásból jönnek az okosságok, hogy minek kell exceptiönnek lenni, és minek visszatérési értéknek. Az én véleményem az, hogy úgy kell megvalósítani, ahogy a legegyszerűbb az adott nyelven megérteni és jól használni az API-t. Az, hogy az exceptiön neve magyarra fordítva kivétel, az nem jelenti azt, hogy csak kivételes esetben kellene használni. És mivel az én megfogalmazásom sem egzakt, nem mérhető mi a legegyszerűbb, ezért többféle megoldás is elfogadható lehet, ízlés kérdése.

Az exceptiönnek az a lényege, hogy a dobása és az elkapása közötti dolgokat át kell ugrani, azok értelműket veszítik az exceptiön miatt. Ez a működése az exceptiönnek az általam ismert nyelvekben, és ebből kell levezetni a használatát is. Ha a "kivétel" miatt a további sorok végrehajtása értelmetlen, akkor ésszerű exceptiönnel megvalósítani, és a megfelelő ponton elkapni, ahonnét van ésszerű recovery a "hibából". Ezt a mechanizmust sokkal egyszerűbb kivétellel megvalósítani, mint esetleg rétegeken keresztül minden metódusba betenni elágazásokat. Ha erre van szükségem, akkor exceptiönt szeretek használni.

További előnye az exceptiönnek, hogy amikor valóban a fejlesztéskor nem várt hiba lép fel, akkor stacktrace-t ad. Erre érdemes figyelni, hogy mindig trace-szel együtt legyen lelogolva a hiba, és akkor segít megtalálni a problémát. Ezt visszatérési értékkel igencsak macerás megcsinálni, a múltkor valaki küldött egy Rustos cikket, abban egyébként éppen ez volt leírva, hogy a "jó hibakezelés Rustban" az, hogy minden réteg hozzáad egy kontextust a hibához, amit ki lehet majd loggolni, és aztán továbbadja ha nem tudja kezelni. Na, ez kísértetiesen hasonlít a stacktrace-re, csak kézzel kell megcsinálni és gagyibb lesz a végeredmény :-)

Még egy lényeges aspektusa az exceptiönöknek, hogy valójában azokban a nyelvekben is van, amikben nincsen. Például a Rust esetén megbeszéltük, hogy túlindexelés vagy nullával osztás lényegében bárhol lehet. Úgy értve, hogy nem zárja ki a nyelv. Vagy statikus ellenőrzéssel kizárod ami végülis megtehető, de akkor visoznt tényleg mindenből jöhet error result, vagy benne marad a rendszerben. Hasonlóan C-ben bármiből jöhet "Invalid Pointer Access" vagy mi is a neve. Nincs exceptiön, de ha ilyet csinálsz, akkor az a szál nem tud tovább futni, tehát ami utána jön a programban az nem fog lefutni. Ebből a szempontból pont olyan mint egy exceptiön. Az exceptiöntől való félelem azért is értelmetlen, mert végeredményben nem lehet kizárni. (Úgy értve, hogy a Rust se zárja ki, elvben ugye egy plusz szabályrendszerrel kizárható lenne.) Tehát szélmalomharcot folytatunk valami ellen, amit inkább ésszerűbb kezelni. A catch-nél el kell gondolkodni, hogy ha az adott blokk nem tudott végigfutni, akkor mi a teendő? Ennek nagyon is van értelme, mert itt dől el a rossz eset stratégiája, ami például a CloudFlare-es megoldásból hiányzott. Hogy a rossz esetben kit kell értesíteni és hogyan, milyen infót kell átadni ahhoz, hogy a hiba hatása a lehető legkisebb legyen. Egy explicit catch segít ebben, hogy a nem várt hiba kezelése és meg legyen tervezve.

Egy példa az exceptiön kivételes használatára: a SAX XML parszer API-n sajnos nincsen megvalósítva az idő előtti kilépés lehetősége. Tehát ha egy XML-t elkezdtél beolvasni, akkor végig kell olvasni, nincs mese! Nekem meg olyat kellett csinálnom, hogy nagyon sok XML fájlból a 2. tag tartalmát kellett kiolvasnom, miközben az XML további tartalma nagy és bonyolult volt, de számomra érdektelen. Hogy csináltam? Az érték megtalálása után lelkifurka nélkül dobtam egy exceptiönt, amit szintén lelkifurka nélkül elkaptam. És ezzel kiugrottam a parszerből a Java nyelv által adott lehetőséggel visszaélve. Nem kell eltartott kisujjú arisztokratának lenni, úgy kell programozni, ahogy megéri. De ez egy kivétel számomra is, a legtöbbször nem kell ilyet csinálni.

Akkor oda-vissza kell ugrálni a táblázat meg a tényleges feloldása közt, és közel se biztos, hogy elsőre szemre megmondod, hol a gond.

Én is jobban szeretem a kifejtett switch-case -ket, pláne olyan nyelvben, ahol amúgy a fgv pointer mint olyan nem létező fogalom, de amúgy is jobban átláthatónak gondolom. Lehet, hogy kódfutás oldalról lehet nyerni vele valamit, de debugolásnál nem a táblázat a jobb.

Blog | @hron84

valahol egy üzemeltetőmaci most mérgesen toppant a lábával 

via @snq-

Vannak, akik arra esküsznek, hogy minimum gyanús, ha egy függvény elejétől a végéig nem fér ki egy képernyőre.

Én jellemzően state machine-t írok switch () case szerkezetben. Az állapotokhoz csinálok egy beszédes enum-ot, s ezek a konstansok szerepelnek a case ágakban. De olyan helyzetben, ahol van sok parancs, amit értelmez a szerkezet, a parancsokat önálló függvényekbe teszem, a függvények címet pedig táblázatba, aztán mérettől és a címtér kihasználtságától függ, hogy közvetlenül indexelem a táblázatot, a nem használt elemek NULL-ok lesznek, vagy bináris, vagy lineáris keresést használok.

Szerk.: IDE-ben Ctrl-függvénynévre kattintás a függvényre ugrik.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Én sosem hagytam, hogy egy elv elcsessze a függvényemet. Ha olyan, akkor hosszú. És? Szerintem fontosabb, hogy logikus blokkok legyenek benne, mint hogy állandóan elugráljak belőle a file egy másik pontjára, nehogy véletlen túl hosszú legyen egy függvény...

Amúgy Sublime Text -et használok, és nagyon sokszor két példányban van megnyitva ugyanaz a forrás file, így simán van, hogy az egyikben beírom a függvény hívását, aztán Alt-Tab és a másik editor ablakban ugyanabba a fileba ezer sorral lejjebb elkezdem írni a a függvényt, közben eszembe jut, hogy célszerű lenne még egy paraméter neki, akkor Alt-Tab, és a hívás helyén beírom a a paramétert, majd Alt-Tab és írom tovább a függvény belsejét.

Lehetne görgetni is, de így nem vész el a kontexus az agyamból. Van amikor három példány is van nyitva ugyanabból a fileból, ha egyszerre három helyen szerkesztem. Ez relatív ritka scenárió, de volt már rá nem egyszer példa. Úgyhogy a hosszú függvény, meg túl nagy állomány, vágjuk-ketté megmondások engem teljesen hidegen hagynak.

Form follows function.

Persze, elvi okokból én sem foglalkozom ezzel. Amikor táblázatba tett függvénypointereket használok, azt azért teszem, mert nekem úgy áttekinthetőbb, szebb, célszerűbb, nem azért, mert alkalmazkodnék az aktuális divathoz.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

És mekkora betűmérettel, ugye? :)

Nyilván egy átlagos programozó átlagos monitorán nem extrém kicsi, és nem nagyon nagy fontmérettel. De ezek többnyire csak soft ajánlások, amelyekkel én személy szerint nem is szoktam egyetérteni, jobban szeretem a szabadságot. Nem is szólnak bele formai dolgokba nálunk a cégnél. Ha a jóra való törekvés megvan, akkor bármit lehet.
 

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Melóhelyen régen volt olyan setup -om, hogy egy normál monitor meg egy 90 fokban elfordított, hogy jó sok szöveg férjen ki rá. Csak azért nem néztek hülyének, mert a kollégák negyedénél ugyanez a nem túl esztétikus, ellenben rettentő hasznos állapot honolt.

A mostani fő gépem (LG Gram 16) kiválasztásánál is plusz pont volt, hogy nem 16:9, hanem 16:10 a képarány. Cirka 2.5 centi plusz magasságot jelent, ami csak addig nem tűnik soknak, amíg mellé nem kerül egy másik 16 collos, 16:9 -es laptop, mert akkor máris kiugrik, hogy mennyivel nagyobb a képe. :-)

Form follows function.

A vélt esztétikumból nem érdemes kiindulni.

Szerintem itt már a premisszáknál félreértés van, a Clean Code nem az esztétikumból indul ki, hanem az olvashatóságból és a karbantarthatóságból.

Persze a Clean Code-osok tagadni fogják, mert ez is ilyen vallás-szerű mint a Rust.

Áh, innen fúj a szél. Nem lehet, hogy neked van valami vallásod a témában? :D

Szerintem a programozás egy kreatív műfaj, mindenki csinálja kedve szerint a szélsőséges trollkodásoktól eltekintve, azaz ne írja például a több 10 kB-os forráskódot egyetlen sorba.

Amíg one man show, addig igen. Amint csapatmunka a programozás, onnantól kezdve a csapat méretével exponenciálisan növekszik a költség, ha mindenki kreatívan fejleszt.

Lehet úgy csapatmunka valami, hogy én megírom egyedül valaminek a firmware-ét, egy másik számítógépen futó egyéb kódokat más nyelven más kollégák írnak, és így tovább. Az esetemben ez a helyzet. Mindamellett szellősen, átláthatóan programozok, csak nem biztos, hogy úgy szeretem lerakni a kapcsoszárójeleket, mint mások. Vagy a *-ot a változó elé írom közvetlenül, nem a típus mögé. Hogy értsd, épp a félreérthetőség elkerülése végett:

char* p, c;

char *p, c;

Ugyanazt jelentik, csak az első eset úgy néz ki, mintha p és c is pointer lenne, pedig csak p az, c nem, és ez épp a második formában látszik jól. De a switch () case : írásakor is más indentációt használok, mint amit ajánlani szoktak. Úgy írom, hogy nekem áttekinthetőbb legyen.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

hogy úgy szeretem lerakni a kapcsoszárójeleket, mint mások

Egy projekten belül az ilyesminek egységesnek kell lennie, és mindenkinek a közös standardhoz kell igazodnia. Pont ez az egyik bajom a CodePilot-tal, hogy  pl PHP-nál egyszerűen nem bírom rávenni a K&R stílusú zárójelezésre, minden alkalommal szopás van belőle, ha kódot generál, hogy hozzá kell formáznom a többihez + a PHPCS se segít sokat.

Addig, amíg one man show a dolog ("én megírom egyedül valaminek a firmware-ét"), addig a lőtéri kutyát nem érdekli a dolog, de abban a pillanatban hogy másoknak akár csak hibakeresniük kell a kódod alapján, már van értelme a közös, csapatszintű standardoknak, amihez mindenki ragaszkodik, és valami statikus analizátor figyelmezteti is a renitenseket.

Blog | @hron84

valahol egy üzemeltetőmaci most mérgesen toppant a lábával 

via @snq-

Erre kell egy slotless (jellegű) plugin a buildbe, vérmérséklettől függő helyen futtatva (minden build, csak teszt futtatáskor, pre-commit hook, CICD pipeline check, stb).   Meglepően sokat segít, hogy kód írás közben erre egyáltalán nem kell figyelni, mert adott ponton úgyis "rendbe lesz rakva" automatikusan.

Írtam több érvet is, hogy miért nem értek egyet vele. Nem vallás. A Clean Code a vallás: Az olvashatóság és karbantarthatóság metrikákat sem támasztja alá mérés. Szerintem még módszertan sincsen kidolgozva rá, hogy hogyan kellene. Gondold el, hogy összemérhető képességű csapatoknak összemérhető méretű és komplexitású programjainak a hosszú távú karbantartási hatékonyságát kellene mérni. Nem tudok olyan esetről, ahol ezt rendesen végigcsinálták volna. A Clean Code anekdotikus érveléssel dolgozik, hogy ez vagy az a módszer jó, az a másik meg nem.

Írtam több érvet is, hogy miért nem értek egyet vele. Nem vallás. A Clean Code a vallás: Az olvashatóság és karbantarthatóság metrikákat sem támasztja alá mérés.

Az érveidet nem támasztja alá semmi, a Clean code mellett pedig van pár study már évek óta. De amúgy hogy lett akkor most az esztétikából hirtelen olvashatóság és karbantarthatóság?

Szerintem még módszertan sincsen kidolgozva rá, hogy hogyan kellene. Gondold el, hogy összemérhető képességű csapatoknak összemérhető méretű és komplexitású programjainak a hosszú távú karbantartási hatékonyságát kellene mérni. Nem tudok olyan esetről, ahol ezt rendesen végigcsinálták volna. A Clean Code anekdotikus érveléssel dolgozik, hogy ez vagy az a módszer jó, az a másik meg nem.

Van egy jó mérés, amit szoktak idézni: https://link.springer.com/article/10.1007/s10664-017-9535-z

És akkor most jön a te részed, hogy keress valami tényt arra, hogy csapatmunkában mennyire előnyös az, ha mindenki összevissza a saját értékrendje szerint kódol. Vagy szimplán magányos farkas vagy, akinek nem kell sok száz másik fejlesztővel együtt dolgoznia?

Vettem a fáradságot és elolvastam a linkelt cikket - oké, volt amit átugrottam benne, de a lényegét megpróbáltam megérteni. A hibák/módosítások számát az osztályokra vetíti. Még csak nem is LOC-ra. És a túl nagy függvény vagy túl nagy osztály is code smell. Lefordítom tehát, hogy mi a konklúziója - és ezt le lehet írni két egy mondatban, felesleges ezért cikket írni: a több sorban általában több a módosítás is.

Ahogy az általam fentebb linkelt példa megmutatja, ugyanaz a program sokkal kevesebb LOC lesz, ha nem követjük a Clean Code elveit. A program eredménye ekvivalens, tehát ugyanannyi logikát kevesebb kód valósít meg. Ha ebből a tömörebb ábrázolásból van több (ezt hívják code smell-nek), akkor az még többet valósít meg. És ami többet valósít meg, abban nyilván várhatóan több a hiba és az egyéb okbóli módosítás is általában. Nem az osztályok számát kell összehasonlítani, hanem a megvalósított funkcionalitás mennyiségét, és arra kellene vetíteni a módosítások számát. Nem mondom, hogy ez könnyű feladat, sőt éppen azt írtam, hogy még módszertanilag definiálni is rohadt nehéz. Ezért nincsenek ilyen cikkek. Amit viszont az általad linkelt cikk csinált, annak semmi értelme nincsen, de legalább kurvasok munkával.

Tényleg végigrágram magam rajta, és sehol nem találtam semmi utalást arra, hogy a módosításszámot legalább LOC-ra vetítenék, mindenütt osztályt ír a cikk. Ha kiderül, hogy legalább LOC-ra vetítik a változások/javítások számát, még akkor se tartunk ott, hogy a megvalósított funkcióra vetítenék, de akkor már egy kicsivel több értelme lenne. Így semmi.

Ez a cikk szerintem iskolapéldája annak, ahogy a mai "tudomány" működik: soktíz oldal bullshit mögé rejtett módszertani hiba. Ha nem olvasod el, csak a konklúzió két mondatát fogod fel, akkor fősodratú mérnök lehetsz, kaphatsz diplomát. Ha nem olvasod el, de be is vallod, akkor te nem tudhatod, mert nem ismered a "tudományt". Ha elolvasod és az utolsó szögig végigrágod, akkor rájössz, hogy bullshit az egész.

WTF? Önkényesen húzol egy vonalat, hogy márpedig LOC legyen a vetítési alap és behozol alátámasztás nélküli állításokat, aztán dobod az egészet?

A program eredménye ekvivalens, tehát ugyanannyi logikát kevesebb kód valósít meg.

Miért jobb, ha kevesebb a LOC?

Ha ebből a tömörebb ábrázolásból van több (ezt hívják code smell-nek), akkor az még többet valósít meg.

Nem, a tömörebb ábrázolás nem code smell, honnan veszel ilyen faszságokat? Olyan hülyeség ez is, mint az az állításd, hogy a Clean Code az esztétikára hajt.

ahogy a mai "tudomány" működik

Ah, szóval újra ott tartunk, hogy ami alátámasztja a világképed, az tudomány, ami nem támasztja alá, az meg "tudomány" lesz. Értem.

>Önkényesen húzol egy vonalat, hogy márpedig LOC legyen a vetítési alap

Az általad hivatkozott cikk az osztályok számából indult ki. Erre mondtam, hogy a LOC szintén nem jó, de ennél annak is több értelme lenne. Megint nem olvasod el amire reagálsz, hanem szalmabábozol. Az általad linkelt cikket legalább elolvastad? Láttad, hogy osztályonként számolták a módosításokat? Szerinted abból kijön valami amikir vannak 20 soros és 20000 soros osztályok us?

Az általad hivatkozott cikk az osztályok számából indult ki. Erre mondtam, hogy a LOC szintén nem jó, de ennél annak is több értelme lenne. Megint nem olvasod el amire reagálsz, hanem szalmabábozol.

Nem, nem olvasok: "Ha kiderül, hogy legalább LOC-ra vetítik a változások/javítások számát, ...", ja, várj, te nem tudod, mit írtál. Szóval továbbra is ott tartunk, hogy nem jó a tanulmány, mert... csak. Mert nem tetszik a tartalma. Meg a napfolttevékenység.

Az általad linkelt cikket legalább elolvastad?

El.

Láttad, hogy osztályonként számolták a módosításokat?

Igen.

 Szerinted abból kijön valami amikir vannak 20 soros és 20000 soros osztályok us?

Igen.

Én most kezdem azt hinni, hogy te mégsem olvastad el, csak bekamuztad, hogy elolvastad.

>És akkor most jön a te részed, hogy keress valami tényt arra, hogy csapatmunkában mennyire előnyös az, ha mindenki összevissza a saját értékrendje szerint kódol.

Ja, erre nem reagáltam. Ez pedig simán szalmabáb. Nem állítottam, hogy az a jó, ha nincsen semmi megállapodás egy projekten. Azt állítottam, hogy a Clean Code konkrétan rossz. Konkrétan több munkát csinál úgy, hogy cserébe lassabb lesz tőle a program. Attól, hogy a Clean Code többségét kitiltod, még lehetnek szabályaid. (Azt hiszem van 1-2 dolog a Clean Code-ban is, amit érdemes csinálni, meg van olyan, amit néha érdemes csinálni, de nem mindig. De többségében bullshit.)

Arról meg pláne nem tettem állítást, hogy én hogyan dolgozok. Ez itt egy elméleti vita arról, hogy mi az optimális módszertan.

Konkrétan több munkát csinál úgy, hogy cserébe lassabb lesz tőle a program.

Ez literálisan nem igaz, kevesebb munkát csinál (ugye a munka része a kód karbantartása is), a hatása általában annyi, hogy több lesz a LOC, ami lófaszt se jelent úgy 20 éve, hogy végül mi lesz a binárisban, a program meg tipikusan nem lesz lassabb. A Clean Code nem arra hajt, hogy szép legyen a kód, hanem arra, hogy karbantartható legyen, hatékony kódot is lehet írni úgy, hogy karbantartható legyen.

Szerintem ott ment félre nálad az egész, hogy a fejedben a "Clean Code" = "szép kód". Pedig a Clean Code egyáltalán nem erről szól.

Arról meg pláne nem tettem állítást, hogy én hogyan dolgozok. Ez itt egy elméleti vita arról, hogy mi az optimális módszertan.

Ja, akkor tegyél erről állítást, hogy hogyan dolgozol.

>a hatása általában annyi, hogy több lesz a LOC, ami lófaszt se jelent úgy 20 éve, hogy végül mi lesz a binárisban, a program meg tipikusan nem lesz lassabb

Az általam linkelt videóban bemutatja Casey Muratori, hogy abban a példában kb 40x gyorsulás érhető el, ha a Clean Code szerint antipattern direkt megoldásra "visszaírja" a programot. El is mondja, hogy szándékosan egy Clean Code bemutató példát választott. Éppen azért, hogy ne lehessen azt mondani, hogy ő találta ki direkt a saját céljaira. Miközben a LOC is jelentősen csökken, és több osztály helyett marad kb egy képernyőnyi kód a végére. Minden metrika szerint jobb az antipattern megoldás, kivéve a sehogyan sem mérhető "karbantartható" metrikát, ami nem metrika, mert nem lehet mérni. Mármint lehetne, de két hozzászólással feljebb írtam, hogy annyira nehéz mérni, hogy eddig nem láttam olyat aki végigcsinálta volna.

A józan ész szerint egyébként nem lehet karbantarthatóbb az, ami több fájra és sokkal több sorra osztja szét azt, ami egyetlen képernyőn is elfér. Ez egyszerűen egy maszlag, amit mindannyian elhittünk. Én is elhittem, csak mások segítségével felismertem a tévedést és már kezdek kigyógyulni belőle.

Amit új irányelvként lehet a Clean Code helyett használni az a Locality of Reference elv. Ennek az a lényege, hogy arra kell törekedni, hogy egy rendszer megértéséhez mennél kevesebbet kelljen ugrálni a kódban, amennyire lehet az összetartozó dolgok egy helyen legyenek. Ez az ami a karbantarthatóságot a legjobban szolgálja. Ezt sem tudom méréssel bizonyítani, de ha belegondolsz van benne logika.

Az általam linkelt videóban bemutatja Casey Muratori, hogy abban a példában kb 40x gyorsulás érhető el, ha a Clean Code szerint antipattern direkt megoldásra "visszaírja" a programot.

Na, ez egy szalmabáb. Remek példa arra, hogy (szerintem szándékosan) félremegy az, hogy mi a Clean Code és hogy kell használni, ugyanis egyáltalán nem tiltja a CC, hogy legyenek dirty részek a programban, a CC alapvetően arról szól, amit már D. E. Knuth is megírt 1974-ben: “premature optimization is the root of all evil”. Általánosságban törekedj arra, hogy a kód karbantartható és jól olvasható legyen, de simán lehet CC kód időkritikus vagy memóriakritikus része dirty, csak legyen megfelelően jelezve és indokolva. Erre írtam már többször, hogy egy hibás premisszára épül ez az egész CC ellenes dolog, nem az esztétika a lényege vagy az, hogy 100 százalékban CC legyen a kód, hanem értelmes keretek között karbantartható legyen.

A józan ész szerint egyébként nem lehet karbantarthatóbb az, ami több fájra és sokkal több sorra osztja szét azt, ami egyetlen képernyőn is elfér.

A "józan ész" igen sokszor csak a "szerintem" szinonimája... most is így használod. 

Amit új irányelvként lehet a Clean Code helyett használni az a Locality of Reference elv.

De ez nincs a CC ellenében, ugyanúgy szerepel benne a "related operation close" elv, a "tight control flow" elv és a "local state" elv. Ugyanúgy része az "encapsulation and cohesion over reuse" elv.

Ez egyszerűen egy maszlag, amit mindannyian elhittünk. Én is elhittem, csak mások segítségével felismertem a tévedést és már kezdek kigyógyulni belőle.

Innen nézve egyszerűen csak rosszul használtad, nem az elveket és a célokat (a miért csináljuk? kérdést) tartottad szem előtt, hanem a szabályokat és a mintákat (hogyan csináljuk? kérdés). Same in Agile, ott is gyakran csak a ceremóniákat és az eszközöket tartják meg betű szerint, de azt, hogy miért agile a csapat, azt nem. Nyilván kudarc lesz belőle.

>Na, ez egy szalmabáb.

Muratori szándékosan egy CC példaprogramot vesz elő, pont azért, hogy ne lehessen szalmabábnak mondani. Hogy lehetne szalmabáb egy iskolapélda kritikája? Ha ez szalmabáb, akkor mutassanak valódi iskolapéldát.

Ha meg már 'érvelési hibázunk', akkor a tied pedig a "nem igazi skót" esete :-).

>egyáltalán nem tiltja a CC, hogy legyenek dirty részek a programban

Ez meg moving goalpost :-). Tehát belátod, hogy a 40x gyorsulás valódi lehet, és akkor most már jön a következő okosság, hogy igen, az valódi, de csak a teljesítménykritikus részeknél számít...

 

A hipotézisem a Clean Code-dal kapcsolatban az, hogy az hogy a programot kisebb egységekre bontjuk azt az érzetet kelti, hogy értjük, mert a kisebb részt tényleg könnyebb egyszerre megérteni. Viszot cserébe ezekből a kicsikből sokkal több lesz, az egészet még kevésbé értjük, mint a "hagyományos" változatot, de az már nem tűnik fel, mert sose látjuk egyszerre azt amit nem értünk. Az eredmény az, hogy sok időt töltünk a probléma széttöredezésével, és utána a komplexebb problémákat sokkal nehezebb kezelni, mint egy egyszerűbb, direktebb megközelítés esetén. Én sem tudom bizonyítani, ne kérd számon ratjam :-)

 

>“premature optimization is the root of all evil”

Ez pedig tekintélyre hivatkozás :-). Ez is egy vitatható tételmondat. Hogyan terveznél mérést annak bizonyítására, hogy valami "root of all evil", vagy nem? Hogyan definiálnád az "evil"-t a számítástudomány keretein belül? :-)

Eleve kérdés, hogy vajon Knuth hogyan értette? Arra gondolt, hogy eleve szándékosan mindent kb 40x lassabbra írunk, vagy arra gondolt, hogy speciális optimalizációkat ne csináljunk előre? Tehát lehet-e ezt a "tételmondatot" a pazarlást alátámasztó érvként használni? És vajon biztosan igaza volt?

Én úgy értelmezem ezt a mondást, hogy optimalizációt lehetőleg mérés alapján csináljunk. Például ha egy adatszerkezetet keresőfa alapon valósítunk meg, de sosincs benne 10-nél több elem, akkor gyorsabb lett volna egy sima tömb. Vegyük észre, hogy ebben az értelmezésben nem is az elvesztett fejlesztési időt siratjuk, hanem azt, hogy a program lassabb lett.

Muratori szándékosan egy CC példaprogramot vesz elő, pont azért, hogy ne lehessen szalmabábnak mondani. Hogy lehetne szalmabáb egy iskolapélda kritikája? Ha ez szalmabáb, akkor mutassanak valódi iskolapéldát.

Ezért írtam zárójelben, hogy ez szándékos dolog a részétől, különben fel kell tételezzem, hogy nem tudja, hogy a CC nem arról szól, hogy mindenáron clean kell legyen a kód. Egyik se vet rá jó fényt: vagy tudatlan, vagy szándékosan ferdít. Szerintem az utóbbi.

Ez meg moving goalpost :-)

Nem, pont erről szól a CC, hogy nem kell 100% clean lennie a kódnak, ha ezt nem érted meg, akkor értem, hogy miért nem érted az egészet.

Tehát belátod, hogy a 40x gyorsulás valódi lehet, és akkor most már jön a következő okosság, hogy igen, az valódi, de csak a teljesítménykritikus részeknél számít...

Hogyne, sehol nem cáfoltam, hogy gyorsabb lehet egy dirty kód, nyilván gyorsabb, pont azért dirty. És igen, a teljesítménykritikus részeknél számít alapvetően, vagy pedig kénytelen leszel vitázni D. E. Knuth állításával is, ami pont erről szól, hogy ne optimalizáljunk előre ok nélkül, fontosabb az olvashatóság és a karbantarthatóság. Ja, már el is kezdted a vitát:

Ez pedig tekintélyre hivatkozás :-)

Oszt? Egy kicsit túltolod a "kérdőjelezzünk meg minden" elvet, berántott a kanál, amikor egy általad kikezdhetetlen életművel szemben annyi áll, hogy "oké, de szerintem". És nincsenek tények és érvek az asztalon.

Én úgy értelmezem ezt a mondást, hogy optimalizációt lehetőleg mérés alapján csináljunk.

Nahát, pont ez van a CC-ben is: legyen a kód alapvetően karbantartható, olvasható és ha vannak kritikus részek, az legyen dirty, csak jelöljük meg, hogy dirty és írjuk le, hogy objektív szempontok szerint miért az.

Nekem még mindig az az érzésem, hogy nem alkalmazod a CC elveit... csak a módszereit, ceremóniáit és szabályait alkalmaztad, mint egy cargo-kultuszt. Csak így nem működik, hogy pont a lényegét nem érted.

Az olvashatóság és karbantarthatóság metrikákat sem támasztja alá mérés.

Azt sem támasztja alá semmilyen mérés, hogy neked miért jó az, ha annyi cukorral iszod a kávét amennyivel. Nem kell mindent mérni ahhoz, hogy elhiggyük, hogy az jó.

De amúgy, ha valami olvasható és karbantartható, az lecsökkenti a hibakereséshez és -megoldáshoz vezető időt. A karbantarthatóság pedig bármilyen javítás vagy feature implementálásakor mérhető. Ha mondjuk van egy kód, amit menet közben alakítottak át karbantarthatóra és/vagy olvashatóra, akkor le lehet mérni, hogy egy-egy hiba átlagos javítási ideje az előző kódon X volt itt meg Y volt, akkor ennyi a nyereség. Nyilván fontos, hogy egy nagyobb batch átlagát vegyük, mert az egyes hibák és javításuk között nagy lehet a szórás, de minél többet nézel, annál jobban kisimul az ebből fakadó kulönbség.

Blog | @hron84

valahol egy üzemeltetőmaci most mérgesen toppant a lábával 

via @snq-

De, a cukorra van mérés és a 0 az egészséges.

Ez a mérés valamire jó, de ki kell annak a hatását szűrni, hogy a csapat megismeri a kódot. Mondjuk az újraírás után rá kell tenni egy friss csapatot.

A lényeg, hogy ha szerinted van ilyen mérés, akkor citáld be, kiváncsian várom.

A szoftver tervezés módszertana egyáltalán nem lezárt tudomány. Ez nem természeti törvény, hogy valami így vagy úgy van és vitának helye nincsen. Már a gondolat is rossz, hogy azért mert könyveket írtak valamiről, azért ne lenne érdemes beszélni róla.

Pontosan!

Például a Clean Code tipikus példái felesleges elbonyolítások, amik a futásidőt, a debuggolhatóságot és az átláthatóságot is rontják. Pont fordítva van, mint ahogy állítja a könyv, sokkal nehezebben átlátható egy clean clode szellemben írt alkalmazás, mint valami ami az egyszerűségre törekszik.

Sok igazság van benne, de alapvetően az a gond, hogy a Clean Code fogalmát, ahány ember, annyi féleképpen értik. Alapvetően nem rossz, de a szellemiségét sok esetben azok sem értik, akik vallásosan alkalmazzák, és azok sem, akik teljesen rossznak tartják. A clean clode szellemben írt alkalmazás, az egyszerűségre törekszik. Ha ez nem igaz, akkor az nem is clean code.

Egy példa: Ha írsz egy 5 oldalas switch case-t, azt bárki át fogja látni pár percen belül. A Clean Code-osok egy 5 oldalas switch case-től agyhúgykövet kapnak, és szétbontják nagyjából 10 osztályra virtuális metódusokkal.

Az 5 oldalas switch case könnyen lehet, hogy nem tökéletes clean code és sokat lehet rajta javítani, de ugyanerre 10 osztály a virtuális metódusokkal, az biztosan nem az, még ha "clean code"-osok ezt is állítják.

A fentiek megértésere ez egy elég jó cikk: The Most Dangerous Lie in Software Engineering (I Believed It For Years) / How following clean code principles blindly creates the exact mess you were trying to avoid /
Esetleg ezt is érdemes megnézni: Clean Code: Four Simple Design Rules – Obligatory Read

Hasonlóan fogom a fejem azon, hogy elvi megfontolásból jönnek az okosságok, hogy minek kell exceptiönnek lenni, és minek visszatérési értéknek.

Azért kell visszatérési értéknek lennie, mert hiába hívják exception-nek, azok olyan hibák, amire számíthatunk, így legalább olyan figyelmet kellene kapjanak, mint a happy path. Amik tényleges exception-ök, azok miatt le kell álljon az alkalmazás, mert nem lehet vele mit kezdeni, nem tud tovább menni semmiképpen sem.

Az exceptiönnek az a lényege, hogy a dobása és az elkapása közötti dolgokat át kell ugrani, azok értelműket veszítik az exceptiön miatt.

Vagy úgy kell szervezni a kódot, hogy a dobás és az elkapás között ne legyenek átugrandó részek.

További előnye az exceptiönnek, hogy amikor valóban a fejlesztéskor nem várt hiba lép fel, akkor stacktrace-t ad.

Ez a hátránya is, mert ez iszonyú lassú művelet. Ha érték szerint van visszaadva a hiba, akkor az ugyanolyan könnyedén kezelhető és érthető, mint a helyes működés. Egyesek ilyenkor azt is állítják, hogy az akkor nem is hiba, mivel nem kell exceptiont dobni. ;-) Pl. elkérünk egy dictionory-ből (Map) egy értéket kulcs szerint. Ha nincs olyan kulcs, akkor némely nyelven exception dobódik. Van ahol ilyenkor értéket ad vissza, pl. Optional-t vagy null-t, utóbbi ugyanolyan rossz, mint az exception. Olyan már ritkábban van, hogy arra ne dobna exception-t, ha a kulcs null. Ezek az esetek mind megelőzhetők. Tehát sok esetben azért kezelünk hibát, mert nem megfelelő a kódunk. Régen a goto nélküli életet sem tudták elképzelni, pedig anélkül is meg lehet oldani mindent, legtöbb esetben olvashatóbb lesz a kód. Ugyanez igaz az exception-re is.

Az exceptiöntől való félelem azért is értelmetlen, mert végeredményben nem lehet kizárni. (Úgy értve, hogy a Rust se zárja ki, elvben ugye egy plusz szabályrendszerrel kizárható lenne.) Tehát szélmalomharcot folytatunk valami ellen, amit inkább ésszerűbb kezelni.

De, a nagy részüket ki lehet zárni, amit nem lehet kizárni, azokat elkapni sem lehet, mert kilépett a program. (Még utóbbiakból is sokat ki lehet zárni ;-)

Egy példa az exceptiön kivételes használatára: a SAX XML parszer API-n sajnos nincsen megvalósítva az idő előtti kilépés lehetősége.

Igen, ez is pont azt bizonyítja, hogy ha valami rosszul van megtervezve, akkor már csak ezek maradnak mentsvárnak. Mivel az enterprise software tervezés eleve egy nagyon rossz koncepció, ezért muszáj is lépten-nyomon exceptiont használni. Igen, én is használok exceptiont, mivel az adatbázis tranzakciót így tudom rollback-elni, illetve sok esetben azt sem tudom, hogy éppen a hívási fa melyik mélységében van az adott kód ami fut.

U.i.: Frankoval nem érdemes vitatkozni, mert vagy el sem olvassa, amit írsz vagy nem képes felfogni, de kimazsoláz belőle dolgokat, amiket jó alaposan megcáfol, ráadásul nem nagyon kulturált  módon.

U.i.: Frankoval nem érdemes vitatkozni, mert vagy el sem olvassa, amit írsz vagy nem képes felfogni, de kimazsoláz belőle dolgokat, amiket jó alaposan megcáfol, ráadásul nem nagyon kulturált  módon.

Ez akkor kölcsönös érzés... fárasztó úgy vitázni, hogy a másik oldal nem olvas, nem ért, utána csak mazsoláz, aztán meg terel. :D

Amúgy innen indultunk, és ezt most épp alátámasztottad:

- a hívott cucc dobjon exception vagy bármi hasonlót, ha a hibát nem az input okozta, hanem az üzemszerűtől eltérő állapota a rendszernek, ami a cucc mögött van, majd a cucc hívója eldönti, hogy mit kezd ezzel az információval,
- adjon vissza értelmes hibaüzenetet és hibakódot, adatokkal, ha minden üzemszerűen működik, de a hibát az input okozta, például nem létező id lekérdezése, a cucc hívója eldönti, hogy mit kezd ezzel az információval.

Mivel vitázunk? Azzal, hogy sokan félreértik a CC-t? Az igaz. Azzal, hogy sokan nem tudják, mi a CC? Az is igaz. Azzal, hogy vannak olyan platformok, ahol túlzottan exception alapú a hibakezelés? Az is igaz.

Azt írtam, hogy nincs olyan (elkapható) exception, ami ne lenne üzemszerű állapot.

Tehát minden (elkapható) exception üzemszerű állapot kell legyen? Mi az üzemszerű állapot a definíciód szerint? Ha betelt a hely, az üzemszerű? Ha nincs hálózati kapcsolat, az üzemszerű? Ha nem érhető el az adatbázis, az üzemszerű? Ha timeout-ra fut a kérés, az üzemszerű? Mi a hibás működés az olvasatodban?

Igen, az én olvasatomban ezek üzemszerű állapotok. Ha elosztott rendszerhez kapcsolódom, akkor várható, hogy nem lesz kapcsolat, hogy időtúllépés történik. Ezek várhatóak és kezelni kell. Ugyanaz, mint amikor ki akarok venni egy Map-ből egy elemet, akkor várható, hogy nem lesz benne. Ha egy pozitív számot várok bemenetnek, akkor várható, hogy nem is szám lesz, vagy negatív, ... Ezek mind üzemszerű esetek. Ezek felmerülésére vagy megelőzésére fel kell készülni.

Egyik kedvenc idézetem: Make invalid states unrepresentable

Legjobb elkerülni a hibákat, hogy ne is következhessen be. Ha viszont nem tudjuk elkerülni, akkor kezdeni kell vele valamit és általában az a legrosszabb, ha nem igazán számítunk rá, csak széttárjuk a karunkat és kiírjuk a hibát a usernek.

Igen, az én olvasatomban ezek üzemszerű állapotok.

Akkor nálad mi a nem üzemszerű állapot? Mert amit felsoroltam, azok nem fedik az üzemszerű állapot fogalmát, üzemszerű állapot akkor van, amikor nincs meghibásodás, üzemzavar vagy akár katasztrófa.

Ha elosztott rendszerhez kapcsolódom, akkor várható, hogy nem lesz kapcsolat, hogy időtúllépés történik.

De ez ettől még nem üzemszerű állapot, hanem egy hibás működés, amit az adott rendszer valahogy megold és/vagy törekszik a megoldására. Ha üzemszerű lenne, akkor maradna úgy, abban az állapotban, ahogy van. A fő választóvonal az üzemszerű és a nem üzemszerű állapot között az, hogy az üzemszerű állapot az, ahova végül eljutunk egy nem üzemszerű állapotból.

Egy autóra se mondod azt, hogy ha nem tudod beindítani a motorját, akkor attól még üzemszerű állapotban van, csak épp kezelni kell, hogy nem indul.

Ezek várhatóak és kezelni kell.

Hogyne, nyilván a nem üzemszerű állapot az egy várható dolog, tudjuk, hogy lesznek eltérések az üzemszerű állapottól, tehát várható dolog az, hogy elromlik valami, de pont az elromlás miatt nem marad üzemszerű állapotban a rendszer. Ha van egy fekete dobozod, ami annyit csinál, hogy letárol egy kulcs-érték párt, annak nem üzemszerű állapota az, hogy épp nem tudja letárolni a kulcs-érték párokat. Nyilván fel kell készülnünk rá, hogy nincs mindig üzemszerű állapotban, de a hibás működése nem üzemszerű állapot.

Ha egy pozitív számot várok bemenetnek, akkor várható, hogy nem is szám lesz, vagy negatív, ... Ezek mind üzemszerű esetek.

Igen, de ez az üzemszerű működés része: nem megfelelő az input. A rendszer, ami megkapja ezt az input-ot, üzemszerűen működik.

Legjobb elkerülni a hibákat, hogy ne is következhessen be. Ha viszont nem tudjuk elkerülni, akkor kezdeni kell vele valamit és általában az a legrosszabb, ha nem igazán számítunk rá, csak széttárjuk a karunkat és kiírjuk a hibát a usernek.

Az exception nem azért van, hogy kiírjuk a hibát a felhasználónak. Hanem arra van, hogy a rendszer képes legyen lekezelni az üzemszerű állapottól való eltérést, teljesen mindegy, hogy ez konkrétan egy exception vagy egy olyan visszaadott érték, amit úgy kell kezelni, mintha exception lenne.

Akkor nálad mi a nem üzemszerű állapot?

Az, ami nem várható. 

De ez ettől még nem üzemszerű állapot, hanem egy hibás működés, amit az adott rendszer valahogy megold és/vagy törekszik a megoldására.

Pont erről beszélgetünk, hogy a hibákat hogyan kell kezelni. ;-)

Igen, de ez az üzemszerű működés része: nem megfelelő az input. A rendszer, ami megkapja ezt az input-ot, üzemszerűen működik.

Ami megkapja az igen, helyesen működik, de ami küldte az nem, ezért küld hibás értéket, nem az elvártat. Ha az adatbázis nem válaszol időben, attól még a fogadó rendszer "üzemszerűen" működik. Egyébként a fogadó rendszer is lehet hibás, ha jó értéket kap, de ő a rosszat várja.

Az exception ... arra van, hogy a rendszer képes legyen lekezelni az üzemszerű állapottól való eltérést

Most döntsd el, hogy mégis mi az üzemszerű eset nálad! Egyik esetben a parser exception, illegal argument exception, not found exception, ... üzemszerű eset, most meg mégsem az.

Akkor nálad mi a nem üzemszerű állapot?

Az, ami nem várható. 

Áh, tehát szerinted az, ha az autó motorja nem indul, az attól még egy üzemszerű állapota az autónak? Ez nem egy hibás állapota, amitől épp nem lehet üzemszerűen használni? Mert teljese mértékben várható, hogy valamilyen hiba miatt valamikor majd nem indul motorja, viszont pont ezért nem lesz üzemszerű a működése a hiba miatt. Az összes tetszőlegesen kicsi kockázattal előre jósolható hibás állapota egy rendszernek akkor szerinted üzemszerű állapot lesz?

 

Pont erről beszélgetünk, hogy a hibákat hogyan kell kezelni. ;-)

Nem, arról beszélgetünk, hogy mi az üzemszerű állapot, amíg ezt nem tudjuk megmondani, addig igen nehéz hibakezelésről beszélni.

Ami megkapja az igen, helyesen működik, de ami küldte az nem, ezért küld hibás értéket, nem az elvártat.

A "helyesen működik" az nálad egy részhalmaza az "üzemszerűen működik" fogalmadnak? Tehát működhet üzemszerűen, de helytelenül?

Ha az adatbázis nem válaszol időben, attól még a fogadó rendszer "üzemszerűen" működik.

Ha nem tudja letárolni az adatot a "fogadó rendszer", amit le kell tárolni, akkor hol van abban az üzemszerű működés?

Most döntsd el, hogy mégis mi az üzemszerű eset nálad!

Ez nem az én döntésem, az "üzemszerű" szónak van egy definíciója, amikor az adott dolgot a rendeltetésének megfelelően tudjuk használni. Ha épp hibás, akkor nincs üzemszerű állapotban.

 Egyik esetben a parser exception, illegal argument exception, not found exception, ... üzemszerű eset, most meg mégsem az.

Ezek exception túlhasználatok, egyik se az én példám, most tartunk ott, hogy rád igaz, hogy "el sem olvassa, amit írsz vagy nem képes felfogni, de kimazsoláz belőle dolgokat, amiket jó alaposan megcáfol".

Ezek exception túlhasználatok, egyik se az én példám, most tartunk ott, hogy rád igaz, hogy "el sem olvassa, amit írsz vagy nem képes felfogni, de kimazsoláz belőle dolgokat, amiket jó alaposan megcáfol".

Nem, sajnos ez most is rád igaz, az összes hozzászólásodon látszik, hogy nem is érted mire válaszolsz. 
Így a saját tanácsomat megfogadom, miszerint nem érdemes veled bármiről is beszélgetni.

Nem, sajnos ez most is rád igaz, az összes hozzászólásodon látszik, hogy nem is érted mire válaszolsz. 

Aham, oké, felírtam: a kis világodban egy motorhiba miatt álló autó üzemszerű állapotban van, ha várható volt a motorhiba.

Így a saját tanácsomat megfogadom, miszerint nem érdemes veled bármiről is beszélgetni.

Lehetne, ha nem írnál faszságot, aztán meg nem ragaszkodnál a faszsághoz, amikor meg ezek miatt végül mégis szembe kellene nézned azzal, hogy faszságot írtál, akkor jön a ragequit...

Ez így általános kijelentésként fasság.

Komplexebb rendszerben simán elképzelhető hogy van pl. több féle de egyenként kicsi valószínűségű szitu, amiből "kikeveredni" 3x annyi kódot jelentene mint egy helyen azt mondani hogy na itt is dobok, ott is dobok aztán kettővel feljebb meg elkapom és lekezelem.

Nem vitatom, hogy van olyan kód, ahol már jobb az exception, mint érték szerint visszaadni. 
Azt vitatom, hogy ne lehetne azt másként csinálni, ahol nem kell exception, mégis egyszerű a kezelése. Írtam példát is, a Functional Core Imperative Shell architektúra.
Az, hogy ez igaz, azt mérhetetlen sok exception mentes nyelven megírt nagy, komplex rendszerek igazolják.

.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Kezdetben volt a GOTO.

Aztán okos emberek rájöttek, hogy sokkal átláthatóbb (olvashatóbb, hibamentesebb) lesz a kód, ha strukturált programozást alkalmaznak.

Kezdetben volt a hiba érték szerinti visszaadása.

Aztán "okos" emberek rájöttek, hogy "sokkal átláthatóbb" lesz a kód, ha egy helyen kezelik a hibákat, így áttértek az ON ERROR GOTO használatára.

Aztán "okos" emberek rájöttek, hogy ha az ON ERROR GOTO-t átnevezik Exception-re, ráadásul még a cél helyet sem kell megadni, hanem bárhol a hívási fában elkapható, akkor még "sokkal átláthatóbb" lesz a kód.

Aztán okos emberek rájöttek, hogy az "okos" emberek rosszul gondolták és úgy lesz átláthatóbb a kód, ha visszatérnek a hiba érték szerinti visszaadására.

Én Atari BASIC -en szocializálódtam. Ott alap volt, hogy a GOTO -nak változóval lehetett megadni, hogy hova ugorjon. Ezért tőlem nem állt messze, hogy menet közben matekoljam ki szorzással és összeadással, hogy merre menjen tovább a program. Később a programozás tanárom a haját tépte, amikor rámutattam, hogy C++ -ban ezt mennyire kényelmetlen asm betéttel megvalósítani. Fura módon az sem tetszett neki, amikor (DOS -os időkről beszélek) a kód szegmensen kicserélgettem bytokat, mert gyorsabban futott az önmódosító kód, mint ha teleraktam volna if -ekkel. Ezekkel a tanárokkal csak a baj van, folyamatosan rontották a hatékonyságomat. :-)

Form follows function.

Már rég volt, de asszem longjmp() -vel ugrottam. Csak valamiért kellett előtte asm, talán regisztereket kellett lementenem, vagy a visszaugráshoz bejegyezni, hogy honnan mentem oda, már nem tudom. De azt tudom, hogy akkoriban nagyon élveztem "varázsolni", imádtam, hogy "mindent lehet". Ezért húzom a szám amikor Ansible jétékkönyvet írok, mert hát ez a nyelv egy béna kacsa.

Form follows function.

Valamint lehet ilyesmit is csinálni, ez most Z80 assembly:

PUSH BC
RET

:)

Jó, mondjuk lehetne JP (HL), ami egy félrevezető mnemonic, tekintve, hogy nem a HL által mutatott címről olvassa fel az ugráscímet, hanem a HL-t teszi a PC-be, azaz oda ugrik, ami a HL-ben van. De ahogy írtam, lehet ezt stack-en keresztül is csinálni, ha a HL épp paramétert adna át.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Reg foglalkoztam mar vele, fejbol irom.

Volt az interrupt (hardware vagy software), olyankor 3 dolog kerulf stackre, a kovetkezo utasitashoz tartozo CS, IP, es a flag regiszter. A flagekbol an interruptot tiltotta, megoldotta amiert hivtak, utana IRET. Ez leszedte a stackrol azt a 3 erteket, visszaallitotta a flageket es visszaugrott ahova kellett. Persze az interrupt handler is hasznalta kozben a stacket ha akarta.

A sima call parja a RET, ott a flageket nem mentette, es az interrupt flaget (cli/sti) is hagyta. Arra mar nem emlekszem, hogy near/far meg milyen egyeb modja volt, es volt-e a visszateresben kulonbseg. Talan x86-on is volt az aktualis cimhez relativ call (signed 8 bit), aztan a near-nel az aktualis CS-en belul maradt, es az IP volt megadva, meg a far-nal a CS:IP is meg volt adva.

(Gimiben asm-eztem utoljara, nem ma volt.)

A strange game. The only winning move is not to play. How about a nice game of chess?

Ah igen... volt a RET meg a RETF. És CALL-ból is volt a near (0xE8) meg a far (0x9A, asszem). Az assembler meg talán a PROC NEAR meg FAR-ból tudta hogy melyikre forditsa... meg azok az ABI-k amik már egy argumentumot is a stack-re tettek... és az stdarg-ot ki lehetett "váltani" pointerarithmetikával... Huh de öszvér volt A régi szép idők :)

Valamikor régen, még Szvatopluk ideje előtt én is csináltam olyat valamelyik BASIC-ben, hogy egy szubrutin végén a GOSUB XXX; RETURN helyett GOTO XXX-et használtam - így a másodjára hívott szubrutin rögtön jó helyre tért vissza.
A tanárok nálam sem értékelték az optimalizáció eme módját.

Sok időt lehet azzal spórolni, ha egy visszatérés nem tér oda vissza, ahol egy ott található visszatérés az eredeti hívóhelyre tér vissza. :)

Pl. ezt használják rekurzív függvények optimalizálásánál is, ilyenkor nem nő a stack, gyakorlatilag ugyanaz, mintha ciklusként lenne megírva.

A lényeg nem az, miként fut, hanem az, hogy miként írjuk/olvassuk. Van (lesz) egy új funkcionális nyelv, amelyik ezt eléggé próbálja kimaxolni. 
Nagyon ígéretes! Roppant egyszerűen írsz benne mindent, automatikus memóriafoglalású, de nincs GC, így nagyon olvasható, nagyon biztonságos, de sebességben a leggyorsabbak között lehet. A cél a GO sebességét űberelni, de azért nem cél a C, Zig, Rust-ot utolérni.