2017-09-26 10 views
8

Jestem obecnie budowa nowego API, a jedna z funkcji dostarcza obecnie brzmi:Czy powinienem preferować MonadUnliftIO lub MonadMask dla funkcji bracketingu?

inSpan :: Tracer -> Text -> IO a -> IO a 

szukam aby przenieść Tracer do monady, dając mi podpis bardziej jak

inSpan :: MonadTracer m => Text -> m a -> m a 

realizacja inSpan wykorzystuje bracket, co oznacza, że ​​mają dwie główne opcje:

class MonadUnliftIO m => MonadTracer m 

lub

class MonadMask m => MonadTracer m 

Ale co powinienem wybrać? Zauważ, że mam kontrolę nad wszystkimi typami, o których wspomniałem, co powoduje, że jestem lekko pochylony w kierunku MonadMask, ponieważ nie wymusza to od dołu (to znaczy, że możemy mieć czystą instancję MonadTracer).

Czy jest coś jeszcze, co powinienem wziąć pod uwagę?

Odpowiedz

19

Niech rozplanować opcje pierwszy (powtarzając niektóre z kwestii w procesie):

  • MonadMask z biblioteki exceptions. Może to działać na szerokiej gamie monad i transformatorów i nie wymaga, aby podstawowa monada była IO.
  • z biblioteki unliftio-core (lub unliftio). Ta biblioteka działa tylko w przypadku monad z bazą IO, która jest w pewnym stopniu izomorficzna z ReaderT env IO.
  • MonadBaseControl z biblioteki monad-control. Ta biblioteka będzie wymagać od bazy IO, ale zezwoli na nie-ReaderT.

Teraz kompromisy. MonadUnliftIO to najnowszy dodatek do walki i ma najmniej rozwiniętą obsługę bibliotek. Oznacza to, że oprócz ograniczeń tego, co monady mogą stanowić instancje, wiele dobrych instancji nie zostało jeszcze napisanych.

Ważne pytanie brzmi: dlaczego MonadUnliftIO sprawia, że ​​ten pozornie arbitralny wymóg dotyczy około ReaderT? Ma to zapobiec problemom z utraconym stanem monadycznym. Na przykład semantyka z bracket_ (put 1) (put 2) (put 3) nie jest zbyt jasna, a zatem MonadUnliftIO nie zezwala na wystąpienie instancji .

MonadBaseControl zmniejsza ograniczenie ReaderT i ma szersze wsparcie dla biblioteki. Uważa się to również za bardziej skomplikowane wewnętrznie niż pozostałe dwa, ale dla twoich zastosowań, które nie powinny mieć znaczenia. I pozwala ci popełniać błędy w monadycznym stanie, jak wspomniano powyżej. Jeśli będziesz ostrożny w używaniu, nie będzie to miało znaczenia.

MonadMask pozwala na całkowicie czyste stosy transformatorów. Myślę, że istnieje dobry argument na temat użyteczności modelowania asynchronicznych wyjątków w czystym stosie, ale rozumiem, że tego rodzaju podejście jest czymś, co ludzie czasami chcą robić.W zamian za zdobycie kolejnych instancji, nadal istnieją ograniczenia związane z monadycznym stanem oraz niemożność podniesienia niektórych działań kontrolnych, takich jak timeout lub forkIO.

Moja rekomendacja:

  • Jeśli chcesz, aby dopasować sposób większość ludzi robi rzeczy dzisiaj, to chyba najlepiej wybrać MonadMask, to najbardziej dobrze przyjęte rozwiązanie.
  • Jeśli chcesz ten cel, , ale, musisz również wykonać timeout lub withMVar lub coś podobnego, użyj MonadBaseControl.
  • Jeśli wiesz, że istnieje określony zestaw monad, z którymi potrzebujesz kompatybilności, i chcesz uzyskać czas kompilacji gwarantujący poprawność twojego kodu względem stanu monadycznego, użyj MonadUnliftIO.
Powiązane problemy