2016-01-04 7 views
6

のは、私はこの(間違いなく誤解を)持っているとしましょう転がっコードの一部:リフトのいずれかを自動的に

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 

EitherExceptTに持ち上げますか? Either StringMonadErrorのインスタンスなので、何らかの方法が必要であると私は感じています。

私は自分の昇降機能を書いた:

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

しかし、私には、私はすでに ExceptTモナド変換子の内部で働いているので、これはまだ間違って感じています。

私はここで間違っていますか?自分のコードを違う構造にする必要がありますか?

+1

「例外」についてはどうですか。戻る? 'ExceptT = ExceptT = m(Either)')、 'return'は' IO(いずれかの文字列ユーザ) 'と 'ExceptT'(コンストラクタ/関数として)を' ExceptT String IO User'にします。 – ibotty

答えて

7

あなたは

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

parseUserの型を一般化することができますし、それはm ~ Either Stringでとm ~ ExceptT String m'手動持ち上げず(Monad m'場合のみ)必要なの両方で動作します。それを行うには

の方法は、基本的にparseUserの定義でthrowErrorreturnLeftRightを交換することです。

+2

'MonadError String m'の制約では、' FlexibleContexts'拡張が必要です(http://stackoverflow.com/a/22795830/905686を参照)。 – user905686