【问题标题】:how to use IO Monad in another Monad如何在另一个 Monad 中使用 IO Monad
【发布时间】:2015-04-02 13:35:13
【问题描述】:

我使用 MongoDB 库来处理来自 Mongodb 的数据。有一个名为 Action 的 Monad 表示 DB 读取或写入操作 https://github.com/TonyGen/mongoDB-haskell/blob/master/doc/tutorial.md。 但是,我发现当我在 monad Action 中时,我也想做一些必须在 IO Monad 中的 IO。一些代码像

-- `Action' is a Monad
--
intoFile :: String -> Cursor -> Action IO ()
intoFile ric c = do
  outH <- liftIO $ openFile ric AppendMode
  liftIO $ hPutStrLn outH "Some log"
  loopIntoFile outH c
  liftIO $ hClose outH

在任何IO monad 之前都有一个liftIO,我认为它可能很冗长。有什么简洁的方法来处理这个吗?

【问题讨论】:

  • 没有真正的好方法来处理这个问题,因为IO 操作仅作为返回IO a 而不是MonadIO m =&gt; m a 来实现。我经常在函数上看到where io = liftIO 之类的东西,并且使用函数io 而不是liftIO,只是为了减少打字。理想情况下,我们应该有hPutStrLn :: MonadIO m =&gt; Handle -&gt; String -&gt; m (),但我们不能指望base 中的函数依赖于第三方库中定义的类(即使这些库是haskell 平台的一部分)。
  • 如果您有一系列 IO 操作,您可以将 do liftIO action1 ; liftIO action2 更改为 liftIO $ do action1; action2 以节省一些提升。

标签: haskell monads io-monad


【解决方案1】:

不幸的是,您无法避免 liftIO,因为标准的 IO 操作不会重载以在任何 MonadIO 中工作。但是您可以在一次调用liftIO 时加入IO 操作序列:

intoFile :: String -> Cursor -> Action IO ()
intoFile ric c = do
  outH <- liftIO $ do
    openFile ric AppendMode
    hPutStrLn outH "Some log"
  loopIntoFile outH c
  liftIO $ hClose outH

或者,如果您打算重复使用相同的IO 操作,您可以为它们引入辅助定义:

intoFile :: String -> Cursor -> Action IO ()
intoFile ric c = do
  outH <- openLog ric AppendMode
  log outH "Some log"
  loopIntoFile outH c
  closeLog outH

openLog path mode = liftIO (openFile path mode)
log handle message = liftIO (hPutStrLn handle message)
closeLog handle = liftIO (hClose handle)

【讨论】:

    【解决方案2】:

    您想要携带 2 个额外的上下文 - IO 上下文和 Action 上下文。 monad 转换器就是这种情况,因为它们允许您处理分层 monad 并在 do 块内部选择哪个 monad 来执行所需的操作。 Here is a great explanation 为什么我们需要它们以及如何使用它们。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-29
      • 1970-01-01
      • 1970-01-01
      • 2022-01-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多