Są tu jeszcze jacyś miłośnicy checked exceptions? Używacie?

Są tu jeszcze jacyś miłośnicy checked exceptions? Używacie?
Zawsze checked exceptions
0%
0% [0]
To zależy (czy możemy jakoś naprawić błąd)
26%
26% [6]
W libkach - tak (bo wtedy mamy ładne API), w aplikacjach - nigdy
0%
0% [0]
Nigdy, to był błąd projektowy Javy
74%
74% [17]
KamilAdam
  • Rejestracja:ponad 6 lat
  • Ostatnio:7 dni
  • Lokalizacja:Silesia/Marki
  • Postów:5505
0

Takie mnie pytanie naszło pod wpływem świata poznanego


Mama called me disappointment, Papa called me fat
Każdego eksperta można zastąpić backendowcem który ma się douczyć po godzinach. Tak zostałem ekspertem AI, Neo4j i Nest.js . Przez mianowanie
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:Koszalin
  • Postów:10094
0

Pomysł fajny, ale zbyt często są nadużywane. Dla przykładu nie rozumiem trochę czemu IOException jest checked.

Użyłbym checked wyjątku, wtedy kiedy można jednoznacznie stwierdzić że wyjątek jest na 100% częścią interfejsu metody. Wtedy bym go dodał, bo jeśli jest częścią interfejsu, to dobrze żeby kompilator o tym wiedział.

Tylko takie przypadki to jest może 1 na 1000. Pozostałe 999/1000 to case'y gdzie wyjątek to część implementacji (nie interfejsu) i nie powinien być checked.

edytowany 1x, ostatnio: Riddle
stivens
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 9 godzin
2

Użyłbym checked wyjątku, wtedy kiedy można jednoznacznie stwierdzić że wyjątek jest na 100% częścią interfejsu metody

Wtedy nie jest wyjatkiem tylko wartoscia Either/Result/WstawCokolwiek


λλλ
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:Koszalin
  • Postów:10094
0
stivens napisał(a):

Użyłbym checked wyjątku, wtedy kiedy można jednoznacznie stwierdzić że wyjątek jest na 100% częścią interfejsu metody

Wtedy nie jest wyjatkiem tylko wartoscia Either/Result/WstawCokolwiek

Nie wartościuję które podejście jest lepsze - czy zwrócenie błędu/either/result czy rzucenie wyjątku. Każdy może stosować co chce.

Ale jeśli mamy metodę, z której leci wyjątek, którego zawsze, ale to zawsze trzeba złapać; bo tak po prostu jest zaprojektowana ta metoda; to wtedy moim zdaniem checked wyjątki są okej.

Jeśli ktoś chce zrefaktorować metodę żeby nie rzucała wyjątków, tylko zwracała Either/Result - be my guest. Ale jeśli nie, i ktoś się zdecyduje zostawić wyjątek, który zawsze trzeba łapać, to moim zdaniem checked wyjątki są okej. Jednak jak wspominałem, to jest 1 na 1000.

jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4709
2
stivens napisał(a):

Użyłbym checked wyjątku, wtedy kiedy można jednoznacznie stwierdzić że wyjątek jest na 100% częścią interfejsu metody

Wtedy nie jest wyjatkiem tylko wartoscia Either/Result/WstawCokolwiek

Dokładnie. A szczególnie bez sensu jest to, że CheckedException ma tak czy siak stacktrace - potencjalnie kosztowny. Po co, skoro wiadomo skąd leci?


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 1x, ostatnio: jarekr000000
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:Koszalin
  • Postów:10094
0
jarekr000000 napisał(a):
stivens napisał(a):

Użyłbym checked wyjątku, wtedy kiedy można jednoznacznie stwierdzić że wyjątek jest na 100% częścią interfejsu metody

Wtedy nie jest wyjatkiem tylko wartoscia Either/Result/WstawCokolwiek

Dokładnie. A szczególnie bez sensu jest to, że CheckedException ma tak czy siak stacktrace - potencjalnie kosztowny. Po co, skoro wiadomo skąd leci?

Nie wiadomo. Przecież to może być z dowolnego miejsca z dołu stacka, i może też być potencjalnie polimorficzny.

A odnośnie tego co @stivens napisał, to chyba istnieje pewne nieporozumienie.

Nie mówię że wyjątki są lepsze niż Either/Result - możecie stosować co chcecie. Ja tylko mówię że jeśli ktoś chce wyjątek (i to jest given), to kiedy ten wyjątek leci zawsze, to checked exceptiony są spoko (zakładając ze trzeba go zawsze złapac). Ale jeśli ktoś nie chce wyjątków, tylko woli either/result to to też jest spoko. Bo jednak w 999 na 1000 przypadków, nie zawsze trzeba go łapać.

edytowany 1x, ostatnio: Riddle
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4709
1
Riddle napisał(a):

Nie wiadomo. Przecież to może być z dowolnego miejsca z dołu stacka, i może też być potencjalnie polimorficzny.

Jeśli trzeba zaglądać do kodu takiego API (numery linii w stack trace), żeby zobaczyć co się stało to i tak jest to słabe API. (a polimorficzność to zupełnie niezależna rzecz).


jeden i pół terabajta powinno wystarczyć każdemu
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:Koszalin
  • Postów:10094
0
jarekr000000 napisał(a):
Riddle napisał(a):

Nie wiadomo. Przecież to może być z dowolnego miejsca z dołu stacka, i może też być potencjalnie polimorficzny.

Jeśli trzeba zaglądać do kodu takiego API (numery linii w stack trace), żeby zobaczyć co się stało to i tak jest to słabe API.

No okej, to faktycznie checked wyjątki mogłyby nie mieć stacktrace'a. Pytanie tylko na ile to byłaby mikrooptymalizacja.

jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4709
1
Riddle napisał(a):

No okej, to faktycznie checked wyjątki mogłyby nie mieć stacktrace'a. Pytanie tylko na ile to byłaby mikrooptymalizacja.

Tak naprawdę to JITy od dawna mocno optymalizują te stack trace'y (np. często nie są faktycznie konstruowane, jeśli nikt ich potem nie czyta).
Ale jest to dość duża komplikacja, która mocno utrudnia (przez komplikację kodu) robienie wielu dalszych optymalizacji.
Podobno nawet Tail Call Optimization jest poniekąd blokowane przez stacke trace i wyjątki (ale nigdy nie do końca zrozumiałem dlaczego, bo imo da się zrobić - po prostu komplikacja).

W praktyce to nawet nie tyle CE co w ogóle powszechność wyjątków w kodzie javy, a API platformy, jest problemem.

Dobry przykład to np. Integer.parseInt, który deklaruje NumberFormatException (co ciekawe nie jest to CE tylko Runtime).
I potencjalnie każda aplikacja, która parsuje jakieś inty może tych exceptionów rzucać dziesiątki tysiący na sekundę.
(kilka razy widziałem problem wydajnościowy na produkcji spowodowany właśnie takimi radośnie rzucanymi wyjątkami).


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 2x, ostatnio: jarekr000000
Zobacz pozostały 1 komentarz
jarekr000000
@KamilAdam: no, ale można "zrekonstruować" sztucznie StackTrace. Nie rozumiem co w tym przeszkadza.
Koziołek
@jarekr000000: przy TCO nie zrekonstruujesz stosu, bo nie masz wiedzy, ile tych rekurencyjnych wywołań już się odbyło. Java wymusza na twórcach, żeby można było odtworzyć StackTrace bez pominięcia wywołań. Już teraz jest to upierdliwe, bo JIT musi zostawiać informacje pozwalające na odbudowę stosu np. przy optymalizacjach typu inline metody.
jarekr000000
@Koziołek: dlaczego nie mam wiedzy ile wywołań się odbyło? Przecież jvm móglby sobie to zwyczjnie zliczać - ot jeden wiecej akumulator.
Koziołek
@jarekr000000: pytanie do łysego pana od JVMa :(
jarekr000000
@Koziołek: czasem mam wrażenie, że łysy pan nie ogarnia jvm na podobnym poziomie co Oderski nie ogarnia fp.
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:Koszalin
  • Postów:10094
0
jarekr000000 napisał(a):

W praktyce to nawet nie tyle CE co w ogóle powszechność wyjątków w kodzie javy, a API platformy, jest problemem.

Z tym się zgadzam. Ale @KamilAdam nie pytał o powszechność wyjątków w stdlib, tylko o checked wyjątki, dlatego chciałem się wypowiedziec na temat.

jarekr000000 napisał(a):

Dobry przykład to np. Integer.parseInt, który deklaruje NumberFormatException (co ciekawe nie jest to CE tylko Runtime).
I potencjalnie każda aplikacja, która parsuje jakieś inty może tych exceptionów rzucać dziesiątki tysiący na sekundę.
(kilka razy widziałem problem wydajnościowy na produkcji spowodowany właśnie takimi radośnie rzucanymi wyjątkami).

No ale w sumie do czego zmierzasz? Wiadomo że jak apka rzuca dziesiątki tysięcy wyjątków na sekundę, to to jest problem, ale to nie wina wyjątków samych w sobie, tylko niepoprawnego użycia ich. Każde narzędzie można nadużyć.

jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4709
2
Riddle napisał(a):

No ale w sumie do czego zmierzasz? Wiadomo że jak apka rzuca dziesiątki tysięcy wyjątków na sekundę, to to jest problem, ale to nie wina wyjątków samych w sobie, tylko niepoprawnego użycia ich. Każde narzędzie można nadużyć.

Tu zmierzam do tego, że stdlib javy niejako taki sposób (Exception Oriented Programming) promuje. Jak nie chcesz wyjątku przy parseInt to trzeba się bawić w jakieś regexpy przed wrzuceniem do parse.
Wiekszość ludzi walnie parseInt. Przed wyjebką na produkcji często nawet nie da się przewidzieć jak często exception będzie leciał, a wystarczy jakaś pomyłka u klienta w kodzie i mamy przypadkowy DOS.

Co gorsza nauczeni patentów z libki standardowej ludzie powtarzają je w swoich aplikacjach.

A wystarczyłoby, żeby parseInt dawało jakiś Either/Result. Zdaję sobie sprawy, że to było pisane w czasach kiedy Eithery były prawie nieznane, a wyjątki to była cool nowość,
po prostu oceniam z perspektywy obecnej.


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 3x, ostatnio: jarekr000000
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:Koszalin
  • Postów:10094
0

No ale to mówisz o problemie wydajnościowym z wszystkimi wyjątkami. I z tym się nie będę kłócił. (mimo że osobiście uważam że jednym z wyjść jest Either, fakt, ale innym wyjściem mogłoby też być isInt(String) który zwraca true/false).

Ale tak czy tak; to odbiega od tematu, bo @KamilAdam nie pytał czy wyjątki są dobrym pomysłem; tylko czy używacie checked wyjątków. Moim zdaniem to inny temat, i trochę odbiegliśmy.

edytowany 2x, ostatnio: Riddle
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4709
1

To jest dokładnie w temacie.

Po prostu uważam, że każde obsługiwalne wyjątki (czyli CE) to błąd.

Wyjątki katastrofalne, których zasadniczo się nie obsługuje (obsługa jest minimalna - np. zwrócenie kodu/stack trace) są ok (i wtedy stack trace jest też ok).

Kolejny argument to też zaśmiecanie języka. To ten sam problem co async czy suspend (z kotlina).
Mamy specjalnie dodany znacznik do typów, który jest związany z jednym rodzajem efektu (a rodzajów efektów jest dużo więcej).
Zresztą throws rozsiewa się w kodzie, w sposób bardzo podobny do async.

Czyli dla mnie to błąd projektowy javy (nieudany eksperyment). (Chociaż z punktu widzenia informatyki, dobrze, że ktoś ten eksperyment zrobił).
Tworzenie kolejnych API z CE tylko przedłuża agonię tego tworu.


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 3x, ostatnio: jarekr000000
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:Koszalin
  • Postów:10094
0
jarekr000000 napisał(a):

To jest dokładnie w temacie.

Po prostu uważam, że każde obsługiwalne wyjątki (czyli CE) to błąd.

Wyjątki katastrofalne, których zasadniczo się nie obsługuje (obsługa jest minimalna - np. zwrócenie kodu/stack trace) są ok (i wtedy stack trace jest też ok).

Czyli jak w kodzię robię catch() na RuntimeException, to jest to błąd według Ciebie?

jarekr000000 napisał(a):

Kolejny argument to też zaśmiecanie języka. To ten sam problem co async czy suspend ( z kotlina).
Mamy specjalnie dodany znacznik do typów, który jest związany z jednym rodzajem efektu (a rodzajów efektów jest dużo więcej).
Zresztą throws rozsiewa się w kodzie, w sposób bardzo podobny do async.

W sumie fakt, aczkolwiek, zaśmiecanie zaśmiecaniu nie równe. Patrząc tylko na "estatykę" języka i kodu, to nie jestem do końca pewny czy Either/Result jest spoko, bo z prostego typu robimy generyk. Wyjątki są jednym ze sposobów ogarnięcia tego.

Poza tym nadal; jedyny argument przeciwko wyjątkom zwykłym jak rozumiem, to jest performance, tak?

stivens
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 9 godzin
1

Wartosci sie tez lepiej komponuje. W CE obsluga bledow jest wymuszona, a jednoczesnie duzo bardziej "pokraczna" niz dla efektow, bo jak masz wartosc to mozesz zrobic np.

Kopiuj
canFailA.orElse(canFailB).fold(defaultValue, _)

EDIT: Jeszcze kilka przykladow: (z dyskusji pod postem)

Kopiuj
canFail
  .logOnError
  .retry(retryPolicy)
Kopiuj
Resource(
  acquire = openFile
)(
  release = file => closeFile(file)
).use(file => readLine(file))
.logOnError

λλλ
edytowany 9x, ostatnio: stivens
Zobacz pozostałe 4 komentarze
stivens
Ale czemu mialbym 10x powtarzac logOnError? I w jaki sposob CE (czy nawet wyjatki in-general) by niby pomoglo?
99xmarcin
Masz 3 operacje wywołać każda inna np. openFile, readLine, closeFile - możesz dać przykład na to. Oczywiście każda może failnąć...
stivens
Czyli chcesz zchainowac te operacje i w przypadku bledu - gdziekolwiek - zalogowac? Resource(acquire = openFile)(release = file => closeFile(file)).use(file => readLine(file)).logOnError
stivens
I pewnie myslales, ze to dobry przyklad na to ze try with finally jest potrzebne, ale nic bardziej mylnego :D
99xmarcin
Tak myliłem się ze składnią "monadyczną" to będzie działać
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:Koszalin
  • Postów:10094
1
stivens napisał(a):

Wartosci sie tez lepiej komponuje. W CE obsluga bledow jest wymuszona, a jednoczesnie duzo bardziej "pokraczna" niz dla efektow, bo jak masz wartosc to mozesz zrobic np. canFail.fold(defaultValue, _) albo canFailA.orElse(canFailB)

TBH nie sądzę że to jest zaleta.

Dlatego że jeśli obsługa błędów jest prostsza z Result, z tymi fold i orElse(), to to zachęca do częstszego używania ich. A moim zdaniem nie powinieneś być bardziej skłonny używać ich; tylko raczej zaprojektować software który po prostu ma tego mniej. I jak masz mało takich miejsc, (np jedno w kodzie), to w sumie co za różnica czy zrobisz .orElse() czy catch.

Zobacz pozostałe 9 komentarzy
Riddle
Przecież dałem Ci konkretny przykład że po prostu upraszczanie składni niewiele pomaga.
stivens
Tzw. "dowod przez machanie rekami". "Bazgrząc jak kura pazurem rysujemy na tablicy gąszcz wykresów, tabelek, wszystkie przyozdabiamy literkami greckimi i machając rękami pokazujemy, że „ze schematu tego wynika oczywistość naszego twierdzenia!”"
stivens
Przecież dałem Ci konkretny przykład że po prostu upraszczanie składni niewiele pomaga - ale JS i DOM g***o ma do tego problemu XD.
Riddle
No ale zasada jest taka sama :| Upraszczanie składni z reguły nie naprawia problemu, a pomaga odpowiednia abstrakcja.
stivens
Machanie rekami.
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4709
1
Riddle napisał(a):

Czyli jak w kodzię robię catch() na RuntimeException, to jest to błąd według Ciebie?

Nie. Robić gdzieś catch(), żeby zalogować problem i zwrócić jakiś błąd klientowi. Natomiast zasadniczo nie obchodzi cię co to byl za błąd, i nie robisz obsługi (choć jakies akcje typu retry są czasem dopuszczalne). Ważne, że liczba tych catch w kodzie jest bardzo mała.

Genealnie RuntimeException to znaczy - pojawił się błąd, którego zdecydowałem się nie obsługiwać, bo to a) pracochłonne b) bezcelowe.
Mogę np. zdecydować, że jak nie mogę połączyć się z bazą danych to rzucam RuntimeException, bo nie widzę sensu kontynuować obsługi żądania. Z drugiej strony świetnie by było, gdyby biblioteka standardowa javy dawała mi możliwość łączenia do bazy bez rzucania wyjątkiem. (bo np. robię generyczny database browser, gdzie URL do bazy jest podawany przez użytkownika - i to, że akurat nic nie ma pod danym URL to normalna sytuacja).

Podobnie z parseInt , mógłbym gdzies stwierdzić, że dany int pochodzi z jakiegoś wewnętrznego konfiga i jeśli nie jest poprawny, to jest to tak absurdalna sytuacja, że używam jakiegoś getOrThrow rzucam exception, a gdzies w najwyższej warstwie jest catch, który to łapie i kończy np. prace systemu (uprzednio rzucając jakieś alerty/logi). Ważne, że podejmuje świadomie tą decyzję i wiem, że np. nie ma szans, żeby ten exception był rzucany tysiące razy na sekunde.

jarekr000000 napisał(a):

Poza tym nadal; jedyny argument przeciwko wyjątkom zwykłym jak rozumiem, to jest performance, tak?

Jeśli chodzi o faktyczne RuntimeException - sytuacje typu panic to zasadniczo nie ma żadnego argumentu przeciwko.
Powinny zdarzać się na tyle rzadko, że wydajność nie ma znaczenia.


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 1x, ostatnio: jarekr000000
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:Koszalin
  • Postów:10094
0
jarekr000000 napisał(a):

Poza tym nadal; jedyny argument przeciwko wyjątkom zwykłym jak rozumiem, to jest performance, tak?

Jeśli chodzi o faktyczne RuntimeException - sytuacje typu panic to zasadniczo nie ma żadnego argumentu przeciwko.
Powinny zdarzać się na tyle rzadko, że wydajność nie ma znaczenia.

Trąci mi premature-optiamlization ten argument.

Dużo jest takich miejsc które są potencjalnie performance'owymi bottleneckami, ale z reguły nie warto ich rozwiązywać dopóki nie wystąpią.

edytowany 1x, ostatnio: Riddle
KamilAdam
  • Rejestracja:ponad 6 lat
  • Ostatnio:7 dni
  • Lokalizacja:Silesia/Marki
  • Postów:5505
1

Argumenty przeciwko wyjątkom

  1. Ukryty przepływ sterowania - może j'bnąć z zaskoczenia
  2. Utrudnione kumulowanie błędów - wywołuje f1, wywołuje f2 jeśli oba się nie powiodą chce zwrócić wspólny błąd. Jeśli jedna to błąd z tej co się nie udała. Jeśli obie się udały to wspólny rezultat
  3. Performance
  4. Zwyczajna brzydkość obsługi

Ad.4. Chce sparsować stringa a jak się nie uda to zwrócić 0

Z wyjątkami

Kopiuj
Long result = 0
try {
  result = parseLong(input)
} catch (RuntimeException e) {
  //logować? nie logować? chyba nie logować. Pewnie się sonar czepi iż nie loguje
}

Bez wyjątków

Kopiuj
var result = parseLongOpt(input).getOrElse(0);

Na szczęście mamy Try więc pewnie więc można zrobić coś takiego

Kopiuj
var result = Try(() -> parseLong(input).getOrElse(0);

Czyli ukryć zakopać, nie używać


Mama called me disappointment, Papa called me fat
Każdego eksperta można zastąpić backendowcem który ma się douczyć po godzinach. Tak zostałem ekspertem AI, Neo4j i Nest.js . Przez mianowanie
edytowany 3x, ostatnio: KamilAdam
99xmarcin
Super duper tylko potem nie działa na prod a w logach nie ma nic...
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4709
1
Riddle napisał(a):

Trąci mi premature-optiamlization ten argument.

Trochę racja, ale to skomplikowane.

Pojedyncze rzucanie exceptionem można wyłapać i naprawić.

Ale tu mówimy o regularnym używaniu w API, rzucaniu potencjalnie tonami exceptionów w normalnym działaniu kodu.

Różne JITy sobie z tym radzą lepiej lub gorzej, ale generalnie stanowi to narzut, trudny nawet do oszacowania. Miałem przykłady, że wywalenie wyjątków (i to nie wszędzie, ale tych najbardziej widocznych w profilingu) zwiększało wydajność o prawie połowę, a czasem nawet wielokrotnie (to był jeden bardzo extremalny przypadek Exception oriented programming, aplikacja działała "jak rzygi" nawet przy jednym użytkowniku). Z tym, że co ważne to raczej dawne czasy jeszcze do javy 8. Obecnie się z tym nie spotykam: a) bo już nie mam kodu z wyjątkami(scala, kotlin), b) JITy sobie lepiej radzą.

Z drugiej, kiedyś będziemy mieli w końcu JIT zrobiony profesjonalnie (czyli przez jakieś AI) - więc faktycznie problem wydajności zniknie całkowicie.


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 3x, ostatnio: jarekr000000
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:Koszalin
  • Postów:10094
0
KamilAdam napisał(a):

Argumenty przeciwko wyjątkom

  1. Ukryty przepływ sterowania - może j'bnąć z zaskoczenia
  2. Utrudnione kumulowanie błędów - wywołuje f1, wywołuje f2 jeśli oba się nie powiodą chce zwrócić wspólny błąd. Jeśli jedna to błąd z tej co się nie udała. Jeśli obie się udały to wspólny rezultat
  3. Performance
  4. Zwyczajna brzydkość obsługi

Nie przekonują mnie te argumenty konkretnie.

Ad. 1. Ukryty, owszem, ale przynajmniej nie masz Result/Either z generykami rozwalonego po kodzie wszędzie. Najgorszy case to jak ten Either jest z jakiejś biblioteki, to wtedy ciężko od niej odejść/zmienić, twarda zależność.

Ad. 2. Imo jeśli chcesz kumulować wyniki to to jest bardzo silny argument za tym że robisz coś co nie powinno być na wyjątkach (np. walidacja). Ludzie mają takie (dziwne) przekonanie, że jak masz walidację (np maila, username'a, url, etc.) to to musi rzucić wyjątek, bo wypada. Czasem nie. Powinno się robić to co ma sens, i dla mnie to że chcesz kumulować "nie udanie się czegoś', to jest bardzo silny sygnał że wyjątki są nienajlepsze do tego. Zwykła List<String> by się lepiej nadała do tego, którą możesz normalnie zwrócić.

Ad. 3 Jeśli faktycznie zmierzysz że są bottleneckiem, to je wywal. Poza tym, to jest kolejny argument o nadużywaniu narzędzia. Ten argument sam w sobie nic nie znaczy, każde narzędzie można nadużyć. Jakbym chciał to mógłbym zrobić Either<String, Either<Either<Integer,Either<String,Boolean>,User>>>>>, i też ktoś mógłby krytykować. Wiadomo że jak ktoś rzuca wyjątek 10000x na sekundę to robi coś źle; ale to nie jest argument przeciwko wyjątkom, tylko przeciwko ich nieodopowiedniemu użyciu.

Ad. 4. Czyli składnia? Mi to wszystko jedno, sam pokazałeś jak łatwo można zmienić try/catch na funkcyjne Try().

jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4709
1
Riddle napisał(a):

Ad. 3 Jeśli faktycznie zmierzysz że są bottleneckiem, to je wywal. Poza tym, to jest kolejny argument o nadużywaniu narzędzia. Ten argument sam w sobie nic nie znaczy, każde narzędzie można nadużyć. Jakbym chciał to mógłbym zrobić Either<String, Either<Either<Integer,Either<String,Boolean>,User>>>>>, i też ktoś mógłby krytykować. Wiadomo że jak ktoś rzuca wyjątek 10000x na sekundę to robi coś źle; ale to nie jest argument przeciwko wyjątkom, tylko przeciwko ich nieodopowiedniemu użyciu.

Jeśli wyjątki są normalną częścią API to niejako dopuszczamy, że ktoś to będzie czasem powodował ich 10000 na sekundę.(podany wyżej przykład parseInt to obrazuje).


jeden i pół terabajta powinno wystarczyć każdemu
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:Koszalin
  • Postów:10094
0
jarekr000000 napisał(a):

Jeśli wyjątki są normalną częścią API to niejako dopuszczamy, że ktoś to będzie czasem powodował ich 10000 na sekundę.(podany wyżej przykład parseInt to obrazuje).

Nie kupuję tego argumentu.

Tak samo mógłbyś powiedzieć o wszystkich innych elementach API, gc(), new Socket(), new Thread(), File.close(), .write(), .clone(), .sort(), i powiedzieć że "skoro są normalne, to dopuszczamy że ktoś ich może wywołać 10000x na sekundę".

Każde jedno narzędzie i funkcja, ma swoje wady i zalety. Nie ma narzędzi które są dobre do wszystkiego i działają zawsze i wszędzie. Każdego narzędzia można nadużyć, i krytykować wynik.

edytowany 2x, ostatnio: Riddle
KamilAdam
  • Rejestracja:ponad 6 lat
  • Ostatnio:7 dni
  • Lokalizacja:Silesia/Marki
  • Postów:5505
0
Riddle napisał(a):

Ad. 1. Ukryty, owszem, ale przynajmniej nie masz Result/Either z generykami rozwalonego po kodzie wszędzie.

Zamieniasz Either na Future/IO i masz reaktywny kod. Jak już umiesz pracować z Either to jesteś w połowie drogi żeby móć wpisać sobie do CV iż znasz się na programowaniu reaktywny,

Najgorszy case to jak ten Either jest z jakiejś biblioteki, to wtedy ciężko od niej odejść/zmienić, twarda zależność.

z vavra? Jedna biblioteka z utilsami to taki koszmar? Czyli jak kiedyś wezmą Either do biblioteki standardowej to to już nie bedzie problem?

BTW to kolekcji z vavra też nie używasz bo to twarda zależność? masz jakąś definicję kiedy można użyć biblioteki zewnętrznej?


Mama called me disappointment, Papa called me fat
Każdego eksperta można zastąpić backendowcem który ma się douczyć po godzinach. Tak zostałem ekspertem AI, Neo4j i Nest.js . Przez mianowanie
edytowany 2x, ostatnio: KamilAdam
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:Koszalin
  • Postów:10094
0
KamilAdam napisał(a):

Najgorszy case to jak ten Either jest z jakiejś biblioteki, to wtedy ciężko od niej odejść/zmienić, twarda zależność.

z vavra? Jedna biblioteka z utilsami to taki koszmar? Czyli jak kiedyś wezmą Either do biblioteki standardowej to to już nie bedzie problem?

Nie koszmar, tylko kalkulacja wad i zalet. Nałożenie swojej abstrakcji często jest tańsze i bardziej pragmatyczne niż rozsianie jej po aplikacji. Jak się okaże że taniej jest użyć bezpośrednio to użyję - jak uznam że nie, to schowam do swojej abstrakcji. To po prostu pragmatyzm.

KamilAdam napisał(a):

BTW to kolekcji z vavra też nie używasz bo to twarda zależność?

Staram się ograniczać poziom do jakiego zewnętrzne biblioteki lub nawet elementy ze standardowej biblioteki panoszą się po kodzie.

Jeśli mam jedno/dwa miejsca, to użyję kolekcji z vavra normalnie. Ale jesli przekazuje jakieś kolekcje pomiędzy wieloma klasami albo wieloma warstwami, to zaenkapsuluję je w coś.

edytowany 1x, ostatnio: Riddle
99xmarcin
  • Rejestracja:około 5 lat
  • Ostatnio:6 miesięcy
  • Postów:2420
0

Jak mawia jeden z wielkich programistów:

Wyjątek łatwo rzucić a ciężko obsłużyć.

Rzucanie wyjątków to przerzucanie odpowiedzialności (i dodatkowej pracy) na klientów naszego kodu. Najlepiej zrobić tak żeby tych wyjątków było jak najmniej.

Checked Exception są dodatkowo bardzo niewygodne w stosowaniu:

  1. Albo wrapujemy ten wyjątek w jakiś ogólny AppException i w takim wypadku checked nie ma sensu bo 90% będzie miało throws AppException
  2. Albo każda metoda ma swój zestaw wyjątków i tworzy to potworne zależności, dodajemy nowy typ wyjątku i potem trzeba go obsłużyć we wszystkich miejscach (to jest dobre). Tyle że jak go obsłużymy? Albo zwrapujemy (czyli wpadamy w (1)) albo dodamy nowy typ wyjątku w metodzie wołającej (czyli wpadamy w (2) i rekruencyjnie musimy dodawać nowy typ wyjątku do każdej sygnatury).

Pewnym rozwiązaniem jest zdefionwanie globalnego zestawu wyjątków i powiedzenie że aplikacja może korzystać tylko z nich. Jest to półśrodek który jako tako działa zwłaszcza gdy pewne typy wyjątków możemy chcieć obsługiwać np. HttpRequestFailedException w kodzie do retry. Zazwyczaj takich typów będzie góra 20 na setki i tysiące metod. Tyle że wtedy throws w sygnaturze traci na znaczeniu bo mapujemy wiele różnych sytuacji na ten sam typ wyjątku np. IllegalArgException raz znaczy że int jest ujemny, raz że indeks jest poza tablicą a raz że nazwa pliku zawiera złe znaki. Czyli robi się misz masz.


Holy sh*t, with every month serenityos.org gets better & better...
GO
  • Rejestracja:około rok
  • Ostatnio:5 miesięcy
  • Postów:358
0

Obsługa błędów wyjątkami jak masz je obsłużyć to jakiś koszmar, zwracanie błędów też koszmar, najlepiej jakby każdy obiekt jeśli dostanie optional nulla po prostu nic nie robił, najwyżej zaloggował do stderr czy gdzieś błąd i zwrócił też nulla.

Jak się robi mega high level abstrakcje to ta obsługa błędów niszczy wszystko, bo jednak trzeba pamiętać o sprawdzeniu błędów, musisz sprawdzić dokumentację tam widzisz, że jak takie i takie coś zwróci to musisz przerwać wykonanie.

A mogło by być tak, że nawet jak błąd wystąpi to nic nie trzeba robić samo się wszystko obsłuży, bo teraz powiedzmy chcemy zrobić taki prosty system drag and drop do obsługi naszych bibliotek to nic nie powinno blokować przepływu danych czy sprawiać, że musimy jakieś błędy sprawdzać, po prostu jak obiekt jest zwrócony optional null to po prostu inne go ignorują i też nullują, a jedynie każdy reportuje błąd.

Ja bym tak podszedł do wszystkiego, bo mam obiekt i coś się nie uda, to muszę jakoś ten błąd zakomunikować, potem go obsłużyć, a mogło by być, że nie trzeba w ogóle przepływu danych zmieniać.
Mnie tam wszystko denerwuje.

Zobacz pozostały 1 komentarz
99xmarcin
NullObject pattern...
jarekr000000
Pamietajcie, że jest specjalne miejsce w piekiełku dla ludzi robiących if (argX == null) return null;
GO
W piekle miejsce jest tylko na ludzi bojących się wprowadzać innowacje @jarekr000000 a co może zrobić obiekt jak dostanie nulla? return -1, exception, null? musi jakoś odpowiedzieć na błąd użytkownika, może też zacząć dzielić przez zero to wtedy procesor jak zobaczy, że ktoś div eax, 0 robi to go zbanuje exceptionem.
jarekr000000
@GodOfC0deee: generalnie domyślną odpowiedzią na null jest rzucenie NullPointerException, co więcej zwykle nic nie trzeba dodatkowego zrobić - sam sie zrobi. Serio.
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:Koszalin
  • Postów:10094
0
.GodOfCode. napisał(a):

Obsługa błędów wyjątkami jak masz je obsłużyć to jakiś koszmar, zwracanie błędów też koszmar, najlepiej jakby każdy obiekt jeśli dostanie optional nulla po prostu nic nie robił, najwyżej zaloggował do stderr czy gdzieś błąd i zwrócił też nulla.

Jak się robi mega high level abstrakcje to ta obsługa błędów niszczy wszystko, bo jednak trzeba pamiętać o sprawdzeniu błędów, musisz sprawdzić dokumentację tam widzisz, że jak takie i takie coś zwróci to musisz przerwać wykonanie.

A mogło by być tak, że nawet jak błąd wystąpi to nic nie trzeba robić samo się wszystko obsłuży, bo teraz powiedzmy chcemy zrobić taki prosty system drag and drop do obsługi naszych bibliotek to nic nie powinno blokować przepływu danych czy sprawiać, że musimy jakieś błędy sprawdzać, po prostu jak obiekt jest zwrócony optional null to po prostu inne go ignorują i też nullują, a jedynie każdy reportuje błąd.

Ja bym tak podszedł do wszystkiego, bo mam obiekt i coś się nie uda, to muszę jakoś ten błąd zakomunikować, potem go obsłużyć, a mogło by być, że nie trzeba w ogóle przepływu danych zmieniać.
Mnie tam wszystko denerwuje.

WTF? 👀

Mi się zdaje, czy Pan nie wiesz o czym Pan mówisz?

99xmarcin
  • Rejestracja:około 5 lat
  • Ostatnio:6 miesięcy
  • Postów:2420
0

Wie o czym mówi, coś ala: https://en.wikipedia.org/wiki/Null_object_pattern

Wtedy null jest też instancja obiektu, w null-aware-Java:

Kopiuj
User u = null; // smart null
u.getName() // zwraca smart null
u.getName().indexOf('foo') // zwraca smart null

Coś ala nested stubs w Mockito.

Wedy jak masz taki "smart null" to można wołać wszystkie metody na obiekcie, a w wyniki zawsze dostaniesz inny smart null.

Tyle że nie znam języka co by to w pełni implementował...


Holy sh*t, with every month serenityos.org gets better & better...
stivens
Kotlin w pewnym sensie u?.name?.indexOf('foo')
GO
O czymś takim myślałem, nie każdemu chce się robić sprawdzanie czy każda wywołana funkcja, obiekt się poprawnie wywołał, a po prostu wywołujesz wszystko i jak coś się nie powiedzie to się nie musisz przejmować. To też jest męczące piszesz kod i musisz wszystko sprawdzić czy się wykonało poprawnie, a tak zawsze mógłbyś na końcu pierwszy zalogowany błąd loggerem wyświetlić na końcu, funkcji, która ma sprezentować wynik.
jarekr000000
a tak zawsze mógłbyś na końcu pierwszy zalogowany błąd loggerem wyświetlić na końcu, funkcji to są dokładnie exceptiony, a w szczególności NullPointerException był i działał już w javie 1.0
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:Koszalin
  • Postów:10094
2

No tylko to jest słabe, bo gdzieś na górze dostajesz taki NullObject, i nie wiadomo czemu jest nullem - która operacja w chainie zawiodła.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.