Jak złe jest uzywanie funkcji `error` w Haskellu? `Control.Monad.Trans.Except` vs `Control.Monad.Except` `MonadError` vs `MonadFail`

1

Cześć, uzbierało mi się kilka pytań odnośnie Haskella

Niby wszystko mi mówi że funkcja error jest zła, ale jest jednak dość często używana w Prelude np dla list [0, 1, 2, 3] !! -1. Kiedy można użyć funkcji error?

Przy założeniu że jednak używamy Either zamiast error to czasem to Either musimy złożyć z jakąś inną monadą. Co jest wtedy lepsze Control.Monad.Trans.Except transformers z czy Control.Monad.Except? mtl Mi bardziej podoba się Control.Monad.Trans.Except, ale Relude domyślnie eksportuje Control.Monad.Except

mtl zawiera monadę MonadError. W jaki sposób jest ona lepsza od MonadFail? A przy założeniu że nie używam własnych errorów tylko i wyłącznie opisy błędów zawsze typu Text?

I jeszcze ostateczne pytanie. Co lepsze MonadError czy ExceptT? Warto brudzić sobie swoją monadę błędami "dziedzicząc" po MonadError? Czy lepiej mieć dwie osobne monady (swoją monadę i ExceptT) ?

Z góry dziękuję za wszystkie odpowiedzi

3

Co do tego kiedy użyć error - nie jestem Haskellowcem (BEAM here), ale moje założenie jest takie:

  • Jeśli otrzymałeś na wejściu dane, które kiedyś mogą zwrócić sukces - Either.
  • Jeśli otrzymałeś dane, które zawsze zwrócą błąd - error. W twoim przykładzie nie ma takiej listy dla której (!! (-1)) zostanie zaaplikowane skutecznie, więc jest error. Nie wiem dlaczego tak samo jest jak jest out of band, zapewne historyczna naleciałość, której nikt nie zmienił od 1998
0
hauleth napisał(a):
  • Jeśli otrzymałeś dane, które zawsze zwrócą błąd - error.

Hm, do takiej sytuacji nie powinno dość. W końcu po coś mamy typy. No ale wtedy ( naszym przypadku listy) index powinien być typu Natural a nie Int. Cała biblioteka standardowa do przeorania :D

Po dwóch dniach refaktoru stwierdzam że pchanie Either jest męczącze :|
I dalej nie wiem jakie są zalety MonadError :(

2

Wydawało mi się, że akurat to że coś istnieje w haskellowym prelude to nie znaczy, że jest dobre : sam o tym piszesz : https://writeonly.pl/haskell-eta/relude#gsc.tab= :-)))
(btw - jest jeszcze RIO).
Cała biblioteka standardowa do przeorania - to jest oczywiste :-) jeśli jesteśmy purystami.

Poza tym niestety nie mam doświadczenia, żeby doradzić.

Ja na to patrze w ten sposób są błędy, które możemy obsługiwać (i te obrabiamy efektywnie jakimś Either) i takie, które uważamy za "end of the world", bo obliczenia doszły do miejsca gdzie albo nie umiemy wyjaśnić co się stało, albo nie chcemy obsługiwać - bo szkoda nam na to czasu - wtedy rzucamy panic/error i kończymy. Nie każdy program musi być idealny i przygotowany na wszystko, wręcz nie powinien. Jakkolwiek to podejście dotyczy aplikacji.

Albowiem jeśli robimy biblioteki to na najniższym poziomie biblioteki powinny raczej dawać możliwość "recovery" bez uciekania się do exceptionów, wyłażenia poza system typów itp. I powinny by default "pchać eithery" - choćby było to męczące.

MonadFail o ile dobrze pamiętam to chyba jest technicznie wprowadzone z tego powodu:

-- | When a value is bound in @do@-notation, the pattern on the left
-- hand side of @<-@ might not match. In this case, this class
-- provides a function to recover.
--
-- A 'Monad' without a 'MonadFail' instance may only be used in conjunction
-- with pattern that always match, such as newtypes, tuples, data types with
-- only a single data constructor, and irrefutable patterns (@~pat@).
--

https://wiki.haskell.org/MonadFail_Proposal

0

Niby piszę, ale pewności czy dobrze piszę nie mam. relude też nie ora mocno. Parę funkcji ukrywa, parę funkcji dodaje. Najwięcej zmienia ustawiając Text jako domyślny typ zamiast String. Ale Już np domyślnie eksportuje listy, czego na szczęście RIO już nie robi. Żadna jednak z nich nie zmienia mocno bibliotek trzecich jak containers czyli zamiana indeksowania typem Int na indeksowanie typem Natural

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