Dlaczego w jednych językach spamowanie wyjątków jest akceptowalne, a w innych nie?

0

Moje (dotychczasowe) rozumienie wyjątku:

Sytuacja bezsensowna na tym poziomie zagłębienia kodu, z którą jednak jakaś metoda (istotnie) wyżej być może może sobie poradzić.

Czyli - przerzucamy kontrolę wyżej. Wyskakujemy nie z tej konkretnej funkcji, w której jesteśmy, ale idziemy w górę stosu tak wysoko, aż (być może) dojdziemy do funkcji, która powie: „z tym problemem mogę sobie poradzić”, czyli zawiera try. Jak takiej funkcji nie będzie, no to crash. Jest to wygodne, ponieważ dzięki temu nie musimy opatrywać ifami każdego wywołania być może problematycznej funkcji, jeśli z problemem i tak nie możemy zrobić nic, jak tylko przekazać go dalej.

if(!tryGetCośtam(out cośtam))
    return false;

doSomething(cośtam);
return true;

vs

var cośtam = getCośtam(); // w razie czego wyjątek pójdzie wyżej
doSomething(cośtam);
return;

W tym drugim przypadku nie musimy zaprzątać sobie głowy ew. problemami, które mogą wystąpić gdzieś niżej, a z ktorymi i tak nie moglibyśmy sobie poradzić.

Nie bez pewnego zdziwienia zauważyłem, że dla wielu to jest przykład TRAGICZNEGO programowania. Nazywają to: „antipattern of exception-driven developmnent”.

W zasadzie ich poglądy można podsumować prosto: Jeśli aplikacja może kontynuować, NIE NALEŻY rzucać wyjątków, gdyż jest to sytuacja „sensowna biznesowo”. Wyjątki służą do scrashowania aplikacji; w innym wypadku należy użyć innych mechanizmów.

Nie rozumiem skąd dokładnie to podejście; ale OK; należy uznać, że nie jestem najmądrzejszy, jeśli bardziej doświadczeni ode mnie tak uważają to pewnie mają powody. (Chociaż nie wszędzie widzę, jak można łatwo zamienić wyjątki z tego na try pattern.

JEDNAK - dziwi mnie, że to jest zależne od języka??

W C# na przykład chyba wyjątki są zakazane, trzeba używać "try pattern". „Vexxing exceptions”.

A w Pythonie jest na odwrót. Tam wyjątki są na kopy. Tam gdzie JA (który i tak spamuję za wiele wyjątków) bym dał sprawdzenie ifem, tam Pythonowcy robią try except.

Zdaje się, że w Pythonie przyjęło się podejście: Zakładaj, że wszystko się uda, jeśli się nie uda to chwyć wyjątek i zajmij się tym.

Czyli, jest dokładna odwrotnośc tego, co się promuje w C# czy Javie.

SKĄD TE RÓŻNICE?!?!?!

Widzę 2 możliwości:

  • To jest kwestia opinii i preferencji. Tak się przyjęło w C#/Javie, a w Pythonie inaczej; ale w zw. z tym nie ma (fundamentlalnych) przeszkód, by spamować wyjątkowami w Javie ani by ich unikać w Pythonie. I jakkolwiek unikanie WTF-ów ludzi przyzwyczajonych do podejścia przeciwnego może być istotnym argumentem, to jednak nie wystarczy to do uzasadnienia absolutyzmu obecnie promowanego twierdzenia, że „exception driven developmnet to antywzorzec”.
  • Istnieją fundamentalne różnice między obsługą wyjątków w Pythonie vs Java/C#. Te fundamentalne różnice sprawiają, że w Pythonie wyjątki mogą być stosowane do control flow, zaś w Javie/C# już nie. ALe jakie to są różnice? Nie wiem. Ani nie wiem, co sprawia, że w Javie/C# nie należy rzucać wyjątków, gdy nie trzeba robić crasha.

Która z tych możliwości jest prawdziwa? Jeśli ta druga, to o czym nie wiem/ czego nie rozumiem? Jeśli żadna, to jak jest naprawdę?

1

Zen Pythona /troche offtop a troche nie/

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
3

Zacznij pisać w C, nie będziesz miał problemów z wyjątkami.

0

Zgaduje, że bierze się to z tego, że w pythonie, a javie piszesz różne rzeczy. Jeśli kod w pythonie ma jakieś 50 linijek i gdzieś rzucisz wyjątkiem i od razu obok go ogarniasz, to łatwiej się połapiesz niż jak w jakimś większym czymś w javie. Gdy zaczniesz "spamować wyjątkami" na lewo i prawo to potem musisz szukać gdzie coś jest obsługiwany, gdzie nie i o co tu w ogóle chodzi.
Ewentualnie dostajesz takie super metody jak void validateX(X x) która rzuca wyjątkiem jeśli walidacja się nie powiedzie, w przeciwnym przypadku nic się nie dzieje.

2

Ile kosztuje wyjątek w C#/Javie, a ile w Pythonie?

Generalnie ja tam wolę stosować defensive programming wszędzie gdzie się da :P

1

Ja tam wale wyjatki gdzie sie da. Ostatni link dobrze to opisuje.

Przykladowo: nie znajde konfiguracji dla danego id wejsciowego.
Ja: wale wyjatek "nie mam konfiguracji dla kodu x"
Developer obok: w takiej sytuacji wystapi przeciez NullPointerException (linijke dalej albo i 300 linii dalej).

Termin "exception driven development" to dla mnie lenistwo intelektualne wynikajace prawdopodobnie z checi zamiecenia calego tematu w jeden kąt. A tak jak to opisano w ww artykule sa rozne rodzaje wyjatkow (takze w javie).

Wyjatkow w metodach typu Int.Parse nie lubie bo nawet jesli zrobisz na to wrapper to bedzie powolny. Do takich niskopoziomowych metod powinien byc odpowiednik z wynikiem w postaci kodu bledu.

3

Wszystko zależy od kontekstu, czy wyjątek dot. sytuacji biznesowej, aplikacyjnej czy może błędu w infrastrukturze, dot. to także miejsca w którym wyłapujesz ten wyjątek. Dla przykładu z "cośtam" możesz to robić dowolnie, kontekst jest najważniejszy, a Ty próbujesz dyskutować tylko o narzędziu. To trochę tak, gdyby na forum budowlańców ktoś się pytał o ciężar młotka (ilość używanych wyjątków), który ma zabierać do pracy i opisywałby rozmiar budynku (język programowania), a nie pracę którą ma wykonać w tym konkretnym dniu (kontekst).

Dużo zależy od programisty raczej a nie języka, chociaż nie mam dużego doświadczenia w Java/Python/C#, w świecie PHP używamy wyjątków - teraz wchodzę w świat Python więc spodziewaj się ich więcej skoro nie było ich za dużo ;)

0
kmph napisał(a):

Nie bez pewnego zdziwienia zauważyłem, że dla wielu to jest przykład TRAGICZNEGO programowania. Nazywają to: „antipattern of exception-driven developmnent”.

Czy masz na myśli to: https://softwareengineering.stackexchange.com/questions/189222/are-exceptions-as-control-flow-considered-a-serious-antipattern-if-so-why ?

0
Silv napisał(a):
kmph napisał(a):

Nie bez pewnego zdziwienia zauważyłem, że dla wielu to jest przykład TRAGICZNEGO programowania. Nazywają to: „antipattern of exception-driven developmnent”.

Czy masz na myśli to: https://softwareengineering.stackexchange.com/questions/189222/are-exceptions-as-control-flow-considered-a-serious-antipattern-if-so-why ?

Tego tekstu akurat nie czytałem, dzięki za linka.

Bardziej miałem na myśli m.in. to: jak rzucic wyjatek 404?

Jak już zalinkowałem to pinguję: @Koziołek , @jarekr000000

0
kmph napisał(a):

Bardziej miałem na myśli m.in. to: jak rzucic wyjatek 404?

W zasadzie nie rozumiem stanowiska @Koziołek z tego postu. Nie programuję w Javie, może dlatego. Wolałbym jednak, by w każdym języku przez "wyjątek" rozumiano to samo. Jeśli przez to pojęcie wyjątku będzie bardzo ogólne – niech będzie. Ale niech rozumie się to samo.

Dla mnie wyjątki są chyba czymś takim jak dla Ciebie, @kmph, i chyba jak dla @Markuz. Jest to próba niemieszania logiki jednej funkcji z logiką drugiej funkcji w przypadku sytuacji, które każą nam te "logiki" mieszać (czytaj: sytuacji błędnych w rozumieniu danej funkcji). Jeśli w kontekście jednej funkcji dana sytuacja jest błędna, to rzucamy wyjątek. Nie mówię o specyficznych mechanizmach konkretnych języków do obsługi "modelu sterowania wyjątkami".

W tym sensie wyjątki są dla mnie czymś przeciwnym do zwracanej wartości. Uważam, że jeśli funkcja zwraca wartość, to ma ona z definicji sens w jej logice.

3

A według mnie wyjątek to jest sytuacja wyjątkowa, nieoczekiwana, która powinna nawet zatrzymać całą aplikację. Trochę kontrowersyjnym przykładem może tu być parsowanie wartości. Czy na prawdę sytuacja w której np do Integer.parse() poda się coś co liczbą nie jest jest aż tak wyjątkowa? W końcu to parsowanie, a nie rzutowanie. Dlatego według mnie wszędzie tam, gdzie coś podczas obliczeń, sprawdzeń itp może pójść nie tak powinno zwracać się nie samą wartość, a opakowaną w jakiś Optional czy coś bardziej złożonego. Z daleka pokazuje to, że nie każde wywołanie tej metody skończy się powodzeniem, a takie int parseInt(String s) bez spojrzenia do kodu, czy dokumentacji nic nie mówi.

Jakieś moje przemyślenia o tym 1 2 3

1

Jeżeli parsowanie pójdzie nie tak z inputu od usera, to normalnie zwracam faila, bo to normalna sytuacja.

Jeżeli parsowanie pójdzie nie tak przy odczytywaniu parametrów configu czy czegoś, to wyjątek, bo nie powinno to mieć miejsca.

0

W zasadzie pytam (jak zwykle ostatnio) w kontekście tego.

Przychodzą dane od usera, czyli JSON oznaczający jego drużynę potworków i umiejętności tychże. Wypadałoby tego JSONa przekonwertować na obiekt klasy Team.

Najpierw chciałem:

if(Team.isValid(teamJson))
{
    Team team = new Team(teamJson);
    doSomething();
}
else
{
    wTeoriiWalnijJakiśBłądAleWPraktycePoProstuZakończPołączenieBoMiSięNieChce();
}

Ale szybko się zorientowałem, że robię copy-pasty: kod Team.isValid oraz konstruktora Team są analogiczne.

Zatem:

try
{
    Team team = new Team(teamJson);
    doSomething();
}
catch (Exception)
{
    wTeoriiWalnijJakiśBłądAleWPraktycePoProstuZakończPołączenieBoMiSięNieChce();
}

Jeśli JSON jest nieprawidłowy, to gdzieś konstruktor albo jakaś jego podprocedura rzuci wyjątkiem. Albo moim, albo bibliotekostandardowym, jak np. Activator.CreateInstance, jeśli nie ma klasy o nazwie potworka, jakiego user chciał.

Potem to się zdegenerowało, bo okazało się, że należy sprawdzać poprawność istotnie przed koniecznością użycia:

try
{
    _ = new Team(teamJson);
}
catch (Exception)
{
    wTeoriiWalnijJakiśBłądAleWPraktycePoProstuZakończPołączenieBoMiSięNieChce();
    return;
}

await zapiszTeamJsonDoBazy(teamJson);

Jak to naprawić bez duplikowania kodu? Pewnie try pattern: zastąpić konstruktor czymś w rodzaju bool Team.tryFromJson(out Team team). WYmagałoby to przejrzenia wszystkich procedur, jakie może wołać konstrutkor, usunąć wyjątki, jakie sam rzucałem na błędach walidacji i upewnić się, że nigdzie już nie opieram się na tym, że jak coś jest źle to funkcja systemowa rzuci wyjątek (jak np. ten Activator.CreateInstance).

Pewnie i można to zrobić, ale po co? Stąd chcę wiedzieć, jakie są konkretne korzyści tego drugiego podejścia nad obecnym.

1
kmph napisał(a):

Ale szybko się zorientowałem, że robię copy-pasty: kod Team.isValid oraz konstruktora Team są analogiczne.

Dlaczego analogiczne? Jedno powinno sprawdzać czy dane są poprawne i ewentualnie zwrócić listę błędów, drugie powinno utworzyć obiekt. Możesz ewentualnie zrobić jakąś statyczną metodę i prywatny konstruktor. Ta metoda zwraca wtedy jakiegoś Either czy coś podobnego albo z obiektem, albo z odpowiednim błędem.

Btw, nie ma jakiś automatycznych mapperów jsona na obiekty? U siebie najpierw wejsciowego jsona mapuje na odpowiednie DTO, a potem dopiero z tego dto tworze obiekt

0

Jeżeli parsowanie pójdzie nie tak z inputu od usera, to normalnie zwracam faila, bo to normalna sytuacja.

Dobry przykład i właśnie niby wydaje się to dobre ale moim zdaniem nie jest dobre do końca. Dlatego że opakowujesz funkcję od razu konkretnym sposobem walidacji. Wyjątki slużą temu by przerwać działanie funkcji z powodu jej nieporpawnego wywołania i zwrócić odpowiedni rodzaj informującego błędu. W tym przypadku który podałeś byłby jakiś ValidationError albo ParseError. Robiąc tak jak w przykładzie jeśli zdarzy się że ktoś inny będzie chciał skorzystać z tej samej funkcji ale inaczej obsłużyć logikę po tym jak zostanie ona niepoprawnie wywołana, będzie częściowo skazany na Twoją walidację (niepotrzebnie). Uważam też że wywoływanie błędów w takich sytuacja jest duzo lepsze jeśli chodzi o debugging.

1

@kmph

Ja bym poszedł w tym kierunku

public class Result<T> where T : class
{
	public Result(T data, string message, bool sucess)
	{
		Data = data;
		Message = message;
		Sucess = sucess;
	}

	public Result(T data, bool sucess)
	{
		Data = data;
		Sucess = sucess;
	}

	public T Data { get; set; }

	public string Message { get; set; }

	public bool Sucess { get; set; }
}

public class Team
{
	public string Name { get; set; }
}


public static class Logic
{
	public static Result<Team> ProcessData(string json)
	{
		var validationResult = ValidateInput(json);

		if (!validationResult.Sucess)
		{
			return new Result<Team>(null, validationResult.Message, false);
		}

		validationResult.Data.Name = "newName";
		SaveChanges();
		return new Result<Team>(validationResult.Data, "Team's name changed properly", true);
	}

	private static Result<Team> ValidateInput(string json)
	{
		Team team;
		try
		{
			team = JsonConvert.DeserializeObject<Team>(json);
		}
		catch
		{
			return new Result<Team>(null, "Json parsing failed", false);
		}

		// business logic checks

		if (team.Name == "Cracovia" && DateTime.Now.Year > 2015)
		{
			return new Result<Team>(null, "Cracovia does not exist after 2015", false);
		}

		return new Result<Team>(team, true);
	}

	private static void SaveChanges()
	{
		throw new NotImplementedException();
	}
}
0

@cmd:
czemu jakaś dodatkowa walidacja? Przecież dokładnie to samo robisz rzucając wyjątkiem, bo i tak musisz zauważyć, że masz jakieś złe wartości.

0

@danek
Nie, rzucając wyjątek przerywasz wykonywanie. Tu mimo wszystko funkcja wykonuje się w cała w pełni poprawnie. To może powodować spore konsekwencję. Bo każdy błąd powinien być obsłużony zgodnie z założeniami. Przykład że funkcja zwraca boola, to ciągle fajny przykład trzymajmy się go. Bo mamy już tu dwie różne wartości przykładowo normalnie zwraca stringa, a jak jest błąd przy parsowaniu to bool że False. Pytanie czy teraz powinienem oczekiwać też że funkcja może zwrócić True w jakimś wypadku? W jakim? Żeby o tym się dowiedzieć musze przeanalizować jej logikę. Może zwraca też inty, kto wie...

4

To niech będzie dalej przykład z parsowaniem inta (bo jest prosty). Która sygnatura mówi więcej?

int parseInt(String s); //w sumie nie wiesz co się stanie bez czytania dokumentacji jak podasz "a"
Optional<Integer> parseInt(String s); //tu brak info o błędzie, ale już wiesz, że jakiś błąd może wystąpić
Either<ParseError, Integer> parseInt(String s); //info o ewentualnym błędzie. Wada: mało popularne podejście

Wiem, że Eithery i tego typu "funkcyjne wynalazki" nie są jeszcze popularne zbytnio i dlatego mogą odpychać. Większość osób nadal woli wyjątki bo są "swojskie". Ale według mnie warto przekonywać do 'nowego' podejścia

1

Wszystko zależy od tego jak się pojmuje wyjątek.

Z mojej perspektywy, walidacja czy przesłane dane są poprawne bądź nie nie zasługuje na miano wyjątku. Patrząc z punktu biznesowego, takie sprawdzenie jest naturalnym przepływem danego programu. Oczywistym jest, że jeśli dane są np. wysłane w niepoprawnym formacie bądź oczekiwałeś minimum 8 znaków, a dostałeś 6 to to stanowi konkretny przypadek biznesowy - po prostu jest on negatywny dla danej funkcjonalności. Inaczej jest w przypadku nie spełnienia kontraktu danego API - jeśli wystawiasz serwis i mówisz, że te i te dane są wymagane, a ktoś ich nie prześle to tutaj już można stwierdzić, że jest to sytuacja wyjątkowa (niekoniecznie trzeba to obsługiwać wyjątkami, ale można).

cmd napisał(a):

Dobry przykład i właśnie niby wydaje się to dobre ale moim zdaniem nie jest dobre do końca. Dlatego że opakowujesz funkcję od razu konkretnym sposobem walidacji. Wyjątki slużą temu by przerwać działanie funkcji z powodu jej nieporpawnego wywołania i zwrócić odpowiedni rodzaj informującego błędu. W tym przypadku który podałeś byłby jakiś ValidationError albo ParseError. Robiąc tak jak w przykładzie jeśli zdarzy się że ktoś inny będzie chciał skorzystać z tej samej funkcji ale inaczej obsłużyć logikę po tym jak zostanie ona niepoprawnie wywołana, będzie częściowo skazany na Twoją walidację (niepotrzebnie). Uważam też że wywoływanie błędów w takich sytuacja jest dużo lepsze jeśli chodzi o debugging.

Obsługiwanie wyjątków w tej samej funkcji jest po prostu błędne. Jeśli zwrócisz typ, który cokolwiek mówi - na przykład Either<UserValidationError, User> to już nie masz tego problemu (modelując UserValidationError jako ADT). W takim przypadku wywołując funkcję widzę typ jaki zwraca i zdaje sobie sprawę co się może zadziać -> mogę dostać albo poprawną instancję klasy User albo błąd walidacji. Jak ten błąd obsłużę? Zależy od miejsca w którym używam funkcji. Jest to zupełnie przeciwieństwo rzucania błędów, które zapewne znajdziesz dopiero po uruchomieniu programu i przejrzeniu wewnętrznej implementacji co dana funkcja może zwrócić.

cmd napisał(a):

Nie, rzucając wyjątek przerywasz wykonywanie. Tu mimo wszystko funkcja wykonuje się w cała w pełni poprawnie. To może powodować spore konsekwencję. Bo każdy błąd powinien być obsłużony zgodnie z założeniami. Przykład że funkcja zwraca boola, to ciągle fajny przykład trzymajmy się go. Bo mamy już tu dwie różne wartości przykładowo normalnie zwraca stringa, a jak jest błąd przy parsowaniu to bool że False. Pytanie czy teraz powinienem oczekiwać też że funkcja może zwrócić True w jakimś wypadku? W jakim? Żeby o tym się dowiedzieć musze przeanalizować jej logikę. Może zwraca też inty w takim wypadku kto wie?

Tak samo możesz przerwać wykonywanie programu poprzez użycie poprawnego efektu. Decydując się na przykładowo Either to w przypadku zwrócenia niepoprawnej wartości (Left) funkcja nie będzie wykonywała się dalej.

Druga sprawa to to, że funkcje często zwracają Bool'a, a wcale nie powinny. Dlaczego? Sam odpowiedziałeś na to pytanie - skąd mam wiedzieć co oznacza False, a skąd mam wiedzieć co oznacza True. Nie żyjemy już w czasach, gdzie trzeba było dbać o jak najmniejsze zużycie pamięci i oszczędzanie zasobów (zapewne w 99% projektów nie ma to żadnego znaczenia). W zależności od kontekstu można na pewno posłużyć się efektem, który poprawnie zamodeluje intencje danej funkcji.

5

Either dla mnie to odpowiednik Checked Exceptions w javie, czyli takich wyjątków które trzeba deklarować i obsługiwać. Praktycznie zawsze można zamienić jedno na drugie. Tylko ze problem z nimi jest taki, że zwykle chcemy je obsługiwać "od razu" i nie bardzo ma sens robienia z tego wyjątku, który przerywa nam control-flow.
Wyjątki runtime to są faktycznie sytuacje wyjątkowe, które wymagają wyskoczenia wiele poziomów w górę bo zwyczajnie nic się nie da zrobić.

Jednoczeście uważałbym z patrzeniem "jak to jest zrobione w XYZ" bo inżynieria oprogramowania cały czas sie rozwija. To co kiedyś wydawało sie OK, mogło się nie sprawdzić i dziś state of the art jest inne. Np. w javie jak robisz get na mapie i nie ma takiego klucza to dostajesz nulla, podczas gdy dziś oczywistym byłoby zwrócenie pustego optionala. Analogicznie jest wiele metod które rzucają wyjątek, a też mogłyby spokojnie ogarnąć to optionalem (np. wszelkie rzeczy w stylu parseInt, Enum.valueOf itd). Taki RestTemplate wali RuntimeException jak dostanie odpowiedź 4xx/5xx, co dla mnie jest absolutnym terroryzmem.

0

Either nie ma jednego, konkretnego zastosowania. Możesz nim zamodelować funkcję która zwraca wynik poprawny bądź błąd, zamodelować coproduct czy po prostu wskazać, że twoja funkcja zwraca dwie możliwe wartości. Nie mniej, jedną z możliwości jest użycie Either do "wyjątków".

Haskell powstał dawno temu i nie wszystko to, co wtedy było akceptowalne i popieranie nadal takie jest. Prostym przykładem jest head z Prelude który może rzucić błędem. Ludzie tworzą własne Preludy, które "naprawiają" te czy inne problemy. Co do sygnatury to zależy jak użyjesz tego Eithera. Jeśli po stronie niepowodzenia będziesz miał ładnie zamodelowane przypadki błędów to z niczym nie musisz się zaznajamiać.

1

Kto mówi cokolwiek o monadach?

Niektóre funkcje które zostały zaprojektowane kiedyś, w chwili obecnej pewnie miałyby inną sygnaturę. Czepiając się tego head'a na liście, zapewne nowa implementacja zwracałaby Maybe (na przykład RIO). Jeśli chciałbyś wywołać head na liście i otrzymać zawsze jakieś a to śmiało, pod warunkiem że operujesz na NonEmpty.

Ja przy pisaniu programu wolę mieć pewność co mogę zrobić na danej strukturze bez wysadzania go w powietrze. Jeśli istnieje szansa na to, że moja lista jest pusta to powinienem obsłużyć oba przypadki. Jeśli z góry wiem, że dana lista nie może być pusta to modeluje ją za pomocą NonEmptyList. head na List nie spełnia tego założenia dlatego z mojej perspektywy jest nie do zaakceptowania. Jeśli ma się coś takiego wysypać to wolę, aby stało się to podczas kompilacji, a nie działania programu. Aczkolwiek to są tylko moje preferencje i nie każdy musi się z nimi zgadzać :)

3

NIe wiem skąd tutaj wyjechał ten Liquidhaskell, jest znany, ale wcale raczej nie jest bardzo popularny. (Trudno powiedziec, badań nie mam).

I nie zastępuje wcale Either, bo raczej się używa do doprecyzowania typów przy walidacjach itp, czyli trochę się uzupełnia braki Haskella. Ale jest to zupełnie ortogonalne do monad.
W Haskellu są w pewnym sensie (w dwóch postaciach) wyjątki, ale te Exceptions na IO to po prostu mniej więcej Either - żadnej magii rzucania tam nie ma. Normalna monada.
Są tez wyjątki typu panic - czyli koniec świata wywalamy się.
I to jest , jak pisał wyżej @Shalom, na sytuacje, gdzie wywaliło się coś na co użytkownik, client nie ma wpływu, nie może zaradzić

Jak wyjątek da się sensownie obsłużyć... to nie jest wyjątkiem i wtedy obrabiamny go Eitherem, czy czymkolwiek podobnym. No i wyszło, że takie podejście sprawdza się również w Javie, Kotlinie i Scali.

0

bardzo fajnie jest to ugryzione w Go. Ogólnie, to przyjęło się, że wszystko co może pójść nie tak i nie jest krytyczne dla całej aplikacji - zwracamy jako błąd (error). Jeśli coś kompletnie się wysypie np połączenie z bazą padło zupełnie, zabrakło pamięci ram itp to walimy panic i często wtedy ten błąd się loguje i restartuje aplikacje.

0
jarekr000000 napisał(a):

Jak wyjątek da się sensownie obsłużyć... to nie jest wyjątkiem i wtedy obrabiamny go Eitherem, czy czymkolwiek podobnym. No i wyszło, że takie podejście sprawdza się również w Javie, Kotlinie i Scali.

Czemu nie w Pythonie?

0

Trochę kwestia comunity. Tutoriale, skrypty itp w pythonie są często pisane przez albo nauczycieli, albo naukowców do celów jakiś prostych wspomagaczy ich pracy: słowem nie do skomplikowanych aplikacji, ze skąplikowaną logiką. Używanie Eitherów itp byłoby dużym overkillem

3
kmph napisał(a):

Czemu nie w Pythonie?

Bo trzeba mnieć system typów / type checker. Bez tego cały koncept siada. Kompilator wymusza wtedy na programiście obsługę sytuacji wyjątkowych - troszkę tak jak w javie wymuszone jest obsługiwanie checked exceptions, tylko mechanizm jest bardziej ogólny i mniej magiczny.
Wprowadzając eithery, optionale, io itp do języka bez type checkera zwiększamy sobie tylko ilość potencjalnych problemów, bo trzeba pilnować i pamiętać w co zapakowany jest rezultat.
Nie, żeby siętego nie robiło - see wszędobylski Promise, Observable w JS.

0

Też wydaje mi się że w Pythonie się to nie sprawdza właśnie przez dynamiczne typowanie. No bo jaki masz pożytek z takiego Optionala skoro i tak go nie widać? Przecież cała przewaga Optionala nad nullem jest taka, ze null jest niewidzialny. Wołasz metodę i nie wiesz czy zwraca nulla czy nie, a jak masz tam dużo poziomów delegacji to może trzeba by kopać kilka poziomów w dół zeby się upewnić. Optional ten problem rozwiązuje bo od razu go widać.
Ale w Pythonie tego nie ma, a przynajmniej często nie ma (bo nikt nie broni używać typizacji i typecheckera ;) ), w efekcie bez różnicy czy zwracasz jakiś Optional czy None, bo na dobrą sprawę i tak trzeba to tak samo obsługiwać. Analogicznie ma się sprawa z Either, cóż ci po nim skoro wołając metodę kompilator nie powie ci co tam dostajesz. Ba, zwrócenie tutaj Either może być strasznie mylące, bo wołasz metodę findXYZ i nagle dostajesz jakieś zupełnie inny obiekt, którego sie nie spodziewałeś.

0

@jarekr000000: @Shalom Mamy 2019 rok od 2 lat ludzie korzystaja z mypy :O

1 użytkowników online, w tym zalogowanych: 0, gości: 1