2016-01-04 19 views
6

Lassen Sie uns sagen, ich habe diese (wohl verleiten) Stück Code rumliegen:Lift Entweder zu Exceptt automatisch

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 gibt mir die folgende Fehlermeldung:

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 

Was ist der normale Weg der Heben Sie eine Either in eine ExceptT? Ich fühle, dass es einen Weg geben muss, da Either String eine Instanz von MonadError ist.

Ich schrieb meine eigene Hebefunktion:

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

Aber mir das fühlt sich immer noch falsch, da ich bereits in der ExceptT Monade Transformator arbeiten.

Was mache ich hier falsch? Soll ich meinen Code anders strukturieren?

+1

Was ist mit 'AusgenommenT. zurückkommen? 'ExceptT = ExceptT (m (Entweder e a))', so bringt 'return' Sie zu' IO (Entweder String User) 'und' ExceptT' (als Konstruktor/Funktion) zu 'ExceptT String IO User'. – ibotty

Antwort

7

Sie können parseUser ‚s Typ

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

verallgemeinern und dann wäre es sowohl bei m ~ Either String arbeiten und bei m ~ ExceptT String m' (wenn auch nur Monad m') ohne manuelles Heben erforderlich.

Die Art und Weise, es zu tun ist im Grunde mit throwError in parseUser ‚s Definition Right mit return und Left zu ersetzen.

+2

Beachten Sie, dass für den Contraint 'MonadError String m' ghc die' FlexibleContexts' Erweiterung benötigt (siehe http://stackoverflow.com/a/22795830/905686). – user905686