Odpowiadam w nowym poście, bo na komentarze musiałbym strasznie dużo nein, nein nein napisać. A tak zbiorczo:
A wyjątek też możesz po złapaniu przekazać jako obiekt w dół, lecz to już jest pewne zboczenie :-p - nohtyp 34 minuty temu
Either przekazuje problem lub obiekt. Więc w dół przekażesz albo wartość, albo dlaczego się nie udało. W przypadku wyjątku :
- w góre dostajesz albo wartość, albo wyjątek. (gicio),
- w dół? Możesz sobie zrobić Pair<Exception,T>... i właśnie zrobiłeś Eithera. Tylko kiepskiego.
Jeśli już pada argument o wydajności (na który czekałem) to znak, że liczba argumentów się skończyła :-) - nohtyp 24 minuty temu
Podałem to jako trzeci i dla mnie najmniej ważny argument. Niemniej, nie jest nonsensowny - dwa razy miałem do czynienia z systemami, które umarły na produkcji przez Exception Oriented Programming
.
Słyszałem o wielu więcej takich przypadkach. Jest to dość częsty problem i oólnie rzutuje na dalszy rozwój JVM (np. rekursja ogonowa, a wyjątki). Dużo systemów rzuca pod spodem tonami niepotrzebnych (bo obsłużonych) exceptionów.
Ja bym tylko na rzecz monad dodał taki argument: monady dają Ci api, które naprawdę ciężko jest nadużyć i jednocześnie nie zauważyć. To fajne jeśli pracujesz z debilami. Natomiast wyjątki są i tak lepsze, ale niestety w firmach nikogo nie widziałem, kto by je mądrze projektował i używał z główą :-( - nohtyp 21 minut temu
Monad też można źle użyć i to bardzo. Szczególnie w językach imperatywnych Java, Kotlin, Scala. Typowo pierwsze użycie optionala to zwykle totalny Rak. (isPresent
, get
).
W sumie można powiedzieć, że wyjątki lepiej sprawdzają sie w implementowaniu bibliotek, natomiast do rzeźbienia w brązie lepiej mogą (chociaż nigdy tego nie widziałem) wypaść monady. - nohtyp 20 minut temu
Dokładnie nie. Obecne wyjątki są katastrofą przy implementacji bibliotek.
Przykład :driver SQL, który gdzieś tam ma jakieś executeQuery(String sql);
Co ta metoda ma zwracać?
Runtime exception? Ale wtedy nie wiadomo (z sygnatury) co przechwytywać, a może ktoś robi jakiegoś SQL Explorer i chce użytkownikowi pokazać co się dzieje.
A na takim poziomie zwykle checmy obsłużyć błąd.
Obecnie w javie jest w tej sytuacji użyty Checked exception, który np. zawsze zrobi nam tego stack trace... nawet jeśli mamy go (stack trace) w poważaniu.
Czasami fajnie jak Error to jakiś enum lub coś podobnego. Możemy sobie enuma wbić jako pole Exceptiona, lub zasymulować enuma hierarchią Exceptionów, ale zwykle widać od razu, że to lekka przesada jest.
Jeśli teraz na bazie tego Statement.executeQuery()
robimy sobie wyższego poziomu funkcję ( typu getUserFromDB()
) to wiadomo, że SQLException znaczy tyle, że
ktoś coś zrypał w kodzie, nie ma połączenia z bazą danych lub coś podobnego. Raczej nic, co obchodzi wywołującego getUserFromDB()
i z czym ten ktoś mógłby sobie poradzić. Więc najlepiej zasiać panikę i rzucić RuntimeException
, bo pewnie złożenie się kawałka procesu, który to wykonał to i tak najlepsza opcja.
W podejściu z Either, SQL.executeQuery powinien zwracać nam Eithera, którego albo obsługujemy od razu, bo chcemy użytkownikowi pokazać co jest problemem. (Jak np.: pisze z palca SQL)
Ewentualnie zamieniamy na RuntimeException
jeśli to lży w naszym kodzie i SQL powinien być dobry
(tylko coś nie wyszło :-) ). Wtedy dostaniemy Exception, z tym samym opisem błędu, ale ze stack trace
zaczynającym się, nie gdzieś w czeluściach SQL drivera, tylko w metodzie, która to wywołała (i pewnie ma błąd).
Obecnie część bibliotek, żeby nie tworzyć tych sztucznych CheckedException zwraca jakieś KODY BŁEDU lub specjalne obiekty (Status, Result).
Przykład przykry: Załóżmy, że robisz jakieś indeksowanie dysku i chcesz po kolei przeczytać każdy plik ( wiadomo, że do wielu z nich nie dostaniesz dostępu...)
Files.newInputStream rzuca Ci wyjątkiem 200 razy na sekundę. Może się okazać, że przez to rzucanie wyjątkami cały ten proces trwa kilka razy dłużej nić gdyby ( jak dziadowie w C
) mieć po prostu status otwarcia pliku....