4

モナドとモナド変換子をつかむのに苦労しています。次の不自然な例があります(コンパイルできません):

import Control.Monad
import Control.Monad.Error
import Control.Monad.Reader

data State = State Int Int Int
type Foo = ReaderT State IO

readEither :: String -> Either String Int
readEither s = let p = reads s
           in case p of
               [] -> throwError "Could not parse"
               [(a, _)] -> return a

readEitherT :: IO (Either String Int)
readEitherT = let p s = reads s
          in runErrorT $ do
    l <- liftIO (getLine)
    readEither l

foo :: Foo Int
foo = do
  d <- liftIO $ readEitherT
  case d of
       Right dd -> return dd
       Left em -> do
     liftIO $ putStrLn em
     return (-1)

bar :: Foo String
bar = do
  liftIO $ getLine

defaultS = State 0 0 0

readEither の機能を readEitherT にコピーすると機能しますが、既存の readEither 関数の機能を活用できるのではないかとしつこく感じていますが、その方法がわかりません。readEitherT 関数で readEither を持ち上げようとすると、必要に応じて持ち上げられErrorT String IO (Either String Int)ます。しかし、どうにかして に到達する必要がありErrorT String IO Intます。

これで間違った方向に進んでいる場合、IO(または他のモナド)を必要とし、モナドコンテキストから呼び出されるエラーを処理する正しい方法は何ですか(foo例の関数を参照)

編集: どうやら、私がやろうとしていたことは明らかではありませんでした。たぶん、次の関数は、私が疑問に思っていたことと理由を説明しています

maybePulseQuit :: Handle -> IO (Either String ())
maybePulseQuit h = runErrorT $ do
  f <- liftIO $ (communicate h "finished" :: IO (Either String Bool))
  (ErrorT . pure) f >>= \b → liftIO $ when b $ liftIO pulseQuit

これは機能しますが、バインドのためにまだ醜いです。これは、大文字と小文字をチェックしていた以前のバージョンよりもはるかに明確です。これはこれを行うための推奨される方法ですか?

4

1 に答える 1

2

なぜ必要なのかは明確ではありませんErrorTreadEitherTあなたは次のように実装することができます

readEitherT :: IO (Either String Int)
readEitherT = fmap readEither getLine

ErrorT何らかの理由で本当に必要な場合は、ユーティリティ関数を作成できeitherToErrorTます。

eitherToErrorT = ErrorT . pure

readEitherT = runErrorT $ do
  l <- liftIO $ getLine
  eitherToErrorT $ readEither l

[追加]ErrorTモナドスタック に追加したいだけかもしれません...

data State = State Int Int Int
type Foo = ErrorT String (ReaderT State IO)

runFoo :: Foo a -> State -> IO (Either String a)
runFoo foo s = runReaderT (runErrorT foo) s

doIt :: Int -> Foo Int
doIt i = if i < 0
            then throwError "i < 0"
            else return (i * 2)

例:

*Main> runFoo (doIt 1 >>= doIt) (State 0 0 0)
Right 4
*Main> runFoo (doIt (-1) >>= doIt) (State 0 0 0)
Left "i < 0"
于 2010-11-16T09:48:31.673 に答える