Ten guard to nie tylko "return early" vs. "one return"?
ale też dotyczy sposobu ułożenia przerwań. Pisanie pod guard będzie sprawiało, że kod obsługujący argumenty będzie miał rozwinięcie obsługi już na początku. Czyli to znaczy, że pisząc kod od razu trzeba wyliczać problemy, a dopiero pod sam koniec pisać logikę związaną z docelowo poprawnym przypadkiem.
Czyli to krzyżówka 2 tematów:
Temat 1: Odnośnie "return early" vs. "one return"?
:
Moim zdaniem to podejście bardziej wymusza język.
Przy językach imperatywnych, zwłaszcza tych, które mają wyjątki nieodzowne jest poleganie na wielu instrukcjach przerwań. Nie spotkałem w życiu takiego przypadku, aby ktoś w imperatywnym języku zamiast pisać 3 throw w metodzie użył ifologię, by rozpoznać warunek, i pod każdy stworzyć osobny obiekt błędu, a potem rzucić go za pomocą jednego dostępnego throw w metodzie. Bez sensu jest robić dodatkowe referencję w takim przypadku. To już niepotrzebna akrobacja, a jeśli już to sytuacja specjalna, i być może wyszukana optymalizacja.
Przy językach funkcyjnych masz już ograniczenie nałożone z poziomu języka. Dla przykładu w clojure nie masz jawnie pisanego return. Po prostu ostatnia wyliczona wartość jest wynikiem. Takie wykonanie powoduje, że mogę chwycić fragment kodu i wykonać go w innym miejscu (które nie jest funkcją) np. w REPL i wykonać. Jawnie pisany return byłby w takiej sytuacji zbyt formalnym ograniczeniem. Clojure nie jest czysto funkcyjny, umożliwia rzucanie wyjątków więc nadal można korzystać z wielu throw, ale właściwie tutaj obliczenie typu błędu ma większy sens niż pisanie osobno trzech throw.
Temat 2: Co pierw? Optymistyczna ścieżka czy pesymistyczna?
Pesymistyczna ścieżka jest dobra do pisania kodu z efektami, a to dlatego, że samoczynnie zachęca (przynajmniej mnie), aby zastanowić się czy funkcja dałaby radę dalej pociągnąć pracę gdyby odpalono ją po jakiejś awarii, wtedy np. if przed wykonaniem sekcji sprawdza czy czegoś nie ma, bo jak nie ma to coś stworzy. Tu nie byłoby sensu tego jak przesuwać, bo dalsza część kodu bazuje na tym co przywrócił ten if.
Tam gdzie nie mam efektów to zawsze wybieram pozytywną ścieżkę, ponieważ nie znoszę podwójnej negacji, a taka zdarza się jeśli ktoś w już w samej nazwie zmiennej ma zaprzeczenie np. 'invalid' to zapis z tą zmienną na chwilę mnie przyblokuje w myślach. Pozytywne ścieżki najprzyjemniej pisze mi w czasie pisania rekurencji, wtedy kod można przeczytać dosłownie jak opis funkcji, wyrwany niczym ze słownika pojęć. Czy są w ogóle definicje w słowniku języka polskiego, które tłumaczą pojęcie poprzez zaprzeczenie? No właśnie.
Jak widać, krzyżując te dwa tematy, bądź rozpatrując je osobno, właściwie nie ma się wyboru.