2016-01-04 39 views
6

Diciamo che ho questo (forse indurre in errore) pezzo di codice, che in giro:ascensore O per Exceptt automaticamente

import System.Environment (getArgs) 
import Control.Monad.Except 

parseArgs :: ExceptT String IO User 
parseArgs = 
    do 
    args <- lift getArgs 
    case safeHead args of 
     Just admin -> parseUser admin 
     Nothing -> throwError "No admin specified" 

parseUser :: String -> Either String User 
-- implementation elided 

safeHead :: [a] -> Maybe a 
-- implementation elided 

main = 
    do 
    r <- runExceptT parseArgs 
    case r of 
     Left err -> putStrLn $ "ERROR: " ++ err 
     Right res -> print res 

ghc mi dà il seguente errore:

Couldn't match expected type ‘ExceptT String IO User’ 
      with actual type ‘Either String User’ 
In the expression: parseUser admin 
In a case alternative: Just admin -> parseUser admin 

Qual è il modo più standard sollevando un Either in un ExceptT? Ritengo che ci sia un modo in cui Either String è un'istanza di MonadError.

ho scritto la mia propria funzione di sollevamento:

liftEither :: (Monad m, MonadError a (Either a)) => Either a b -> ExceptT a m b 
liftEither = either throwError return 

Ma per me questo si sente ancora sbagliata dato che sto già lavorando all'interno del trasformatore Monade ExceptT.

Cosa sto facendo di sbagliato qui? Dovrei strutturare il mio codice in modo diverso?

+1

E a proposito di "Eccetto". return'? 'ExceptT = ExceptT (m (Either e a))', quindi 'return' ti porta a' IO (Either String User) 'e' ExceptT' (come costruttore/funzione) a 'ExceptT String IO User'. – ibotty

risposta

7

Si può generalizzare tipo parseUser s' a

parseUser :: (MonadError String m) => String -> m User 

e poi avrebbe funzionato sia a m ~ Either String ea m ~ ExceptT String m' (anche se solo Monad m') senza alcun sollevamento manuale necessario.

Il modo per farlo è quello di sostituire praticamente Right con return e Left con throwError nella definizione parseUser s'.

+2

Nota che per il contraint 'MonadError String m' ghc ha bisogno dell'estensione' FlexibleContexts' (vedi http://stackoverflow.com/a/22795830/905686). – user905686