2012-04-04 12 views
5

Chyba brakuje podstawowe zrozumienie transformatorów monady, ponieważ uważam, że ja pisząc ten kod:Podnieś wartość błędu do transformatora monada ErrorT

import Control.Monad.Identity 
import Control.Monad.Error 

liftError :: Either String Int -> ErrorT String Identity Int 
liftError x = do case x of 
        Right val -> return val 
        Left err -> throwError err 

gateway :: Bool -> ErrorT String Identity Int 
gateway = liftError . inner 

inner :: Bool -> Either String Int 
inner True = return 5 
inner False = throwError "test" 

Chociaż to działa, myślę, że może to być zrobione więcej elegancko. W szczególności szukam zamiennika liftError, co moim zdaniem nie powinienem sam określać.

Jaki byłby najprostszy sposób, aby gateway i inner współpracować bez zmiany ich typu?

Odpowiedz

6

Jeśli zmienisz tylko trochę, nie musisz w ogóle wykonywać podnoszenia.

{-# LANGUAGE FlexibleContexts #-} 

gateway :: Bool -> ErrorT String Identity Int 
gateway = inner 

inner :: MonadError String m => Bool -> m Int 
inner True = return 5 
inner False = throwError "test" 

MonadError ma wystąpień zarówno ErrorT i Either, więc w ten sposób można używać zarówno jako inner.

+0

Okej, więc, mówiąc dokładniej, muszę napisać ręczną funkcję podnoszenia, jeśli chcę zostawić typy takimi, jakimi są? To tylko dla mojego zrozumienia. BTW niestety nie mogę dzisiaj głosować:/Otrzymasz głos jutro :) –

+1

Bez zmiany typów możesz użyć 'ErrorT. return' w miejscu twojej funkcji 'liftError'. Możesz także użyć wewnętrznej wersji bardziej ogólnej i ujawnić tylko kopię "wewnętrznego" z zastrzeżonym typem. – hammar

+0

To 'ErrorT. return' było dokładnie tym, czego szukałem. Jak już wspomniałem, nie ma to nic wspólnego z żadnym prawdziwym kodem, po prostu chcę zrozumieć pojęcie transformatorów monadowych :) –