2016-01-04 8 views
6

Допустим, у меня есть это (возможно, ввести в заблуждение) кусок кода прокладки вокруг:лифт Либо Exceptt автоматически

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 дает мне следующую ошибку:

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 

Какой самый стандартный способ поднимая Either в ExceptT? Я чувствую, что должен быть какой-то способ с Either String является экземпляром MonadError.

я написал свою собственную функцию подъема:

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

Но для меня это все еще чувствует себя не так, так как я уже работает внутри монады трансформатора ExceptT.

Что я здесь делаю неправильно? Должен ли я структурировать свой код по-другому?

+1

Что относительно 'ExceptT. return'? 'ExceptT = ExceptT (m (Либо e a))', поэтому 'return' возвращает вас к' IO (любому пользователю String) 'и' ExceptT' (как конструктор/функция) в 'ExceptT String IO User'. – ibotty

ответ

7

Вы можете обобщать тип parseUser «s к

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

, а затем она будет работать как на m ~ Either String и в m ~ ExceptT String m' (если только Monad m') без какого-либо ручного подъема необходимо.

Способ сделать это состоит в основном заменить Right с return и Left с throwError в определении parseUser «s.

+2

Обратите внимание, что для contraint 'MonadError String m' ghc требуется расширение' FlexibleContexts' (см. Http://stackoverflow.com/a/22795830/905686). – user905686