モナドとモナド変換子をつかむのに苦労しています。次の不自然な例があります(コンパイルできません):
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
これは機能しますが、バインドのためにまだ醜いです。これは、大文字と小文字をチェックしていた以前のバージョンよりもはるかに明確です。これはこれを行うための推奨される方法ですか?