monad 转换器上下文中的 Monad

发布于 2024-10-02 20:27:54 字数 1465 浏览 2 评论 0原文

我很难掌握 monad 和 monad 转换器。我有 以下人为示例(不可编译):

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 函数,但我不知道如何实现。如果我尝试举起 readEither 在 readEitherT 函数中,它将其提升为 ErrorT String IO (要么 String Int) 正如它应该的那样。但我应该以某种方式将其传递给 ErrorT 字符串 IO Int。

如果我的方向是错误的,那么正确的方法是什么 处理需要 IO(或其他 monad)并从中调用的错误 一元上下文(请参阅示例中的 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

。它有效,但由于绑定仍然很难看。这比之前有案例检查的版本清晰得多。这是推荐的方法吗?

I have trouble gripping to monads and monad transformers. I have the
following contrived example (not compilable):

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

If I copy the functionality of readEither to readEitherT, it works, but I
have a nagging feeling that I can leverage the power of the existing
readEither function, but I can't figure out how. If I try to lift the
readEither in the readEitherT function, it lifts it to ErrorT String IO
(Either String Int)
as it should. But I should somehow get it to ErrorT
String IO Int
.

If I'm going to the wrong direction with this, what is the correct way to
handle errors which require IO (or other monads) and are to be called from
monadic context (see the foo function in the example)

Edit:
Apparently it was not clear what I was trying to do. Maybe the following function describes what and why I was wondering

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

This works, but is still ugly because of the binds. This is a lot clearer than the previous version which had case checking. Is this the recommended way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

夏の忆 2024-10-09 20:27:54

目前尚不清楚为什么需要 ErrorT。您可以像这样实现 readEitherT

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

如果由于某种原因您确实需要 ErrorT,那么您可以创建实用函数 eitherToErrorT

eitherToErrorT = ErrorT . pure

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

[ADD]< /强>
也许您只想将 ErrorT 添加到您的 monad 堆栈中...

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"

It is not clear why you need ErrorT. You can implement readEitherT like

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

If you really need ErrorT for some reason, then you can create utility function eitherToErrorT:

eitherToErrorT = ErrorT . pure

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

[ADD]
Maybe you just want to add ErrorT into your monad stack...

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)

Example:

*Main> runFoo (doIt 1 >>= doIt) (State 0 0 0)
Right 4
*Main> runFoo (doIt (-1) >>= doIt) (State 0 0 0)
Left "i < 0"
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文