【问题标题】:How to flatten IO (IO ())?如何扁平化IO(IO())?
【发布时间】:2018-12-27 02:41:11
【问题描述】:

我刚刚学习 Haskell 和 monad 转换器,我发现自己有一个 IO (IO ()),我想将其扁平化为 IO ()。我确定我做错了什么,但无法准确指出我迷路的地方。

这是我正在尝试做的一个简化示例。这是实现echo 的复杂方式,但它说明了问题。

userInput :: Monad m => ReaderT (IO String) m (IO String)
userInput = ask

echo :: Monad m => ReaderT (IO String) m (IO ())
echo = userInput >>= \input ->  -- unwrap ReaderT to get an IO String
         input >>= (\s ->       -- unwrap IO String to get a String
           putStrLn s)          -- print out the String
         & return               -- rewrap into a ReaderT

main :: IO (IO ())              -- How to turn IO (IO ()) to IO ()?
main = runReaderT echo getLine

在我的实际应用程序中,我有一个Spock 应用程序,它向上游服务器发出 HTTP 请求。 Spock 应用程序使用名为 SpockCtxT 的 monad 转换器堆栈,我想将 ReaderT 插入堆栈以抽象 HTTP 请求,以便我可以在测试中将其换成模拟实现。

从根本上说,这个想法是一个单子转换器堆栈,其中一个转换器为您提供IO,无论是 HTTP 请求还是getLine。我是在错误地思考这个问题还是有什么办法可以做到这一点?

【问题讨论】:

  • 使用join进行展平。
  • IO (IO ()) -> IO () 是来自 Control.Monad 的 join
  • 旁注:当库导出具有IO (IO ()) 之类类型的内容时,它们通常不希望您使用join,而是使用layeredAction >>= \inner -> somethingElse >> inner 之类的东西

标签: unit-testing haskell monads monad-transformers


【解决方案1】:

问题的答案是join :: IO (IO ()) -> IO ()。但我认为您应该提出的问题的答案是liftIO :: IO () -> ReaderT (IO String) IO ()。像这样:

userInput :: MonadIO m => ReaderT (IO String) m String
userInput = ask >>= liftIO -- this liftIO eliminates your need for join

echo :: MonadIO m => ReaderT (IO String) m ()
echo = userInput >>= liftIO . putStrLn -- this liftIO is just so you can use putStrLn in ReaderT

main :: IO ()
main = runReaderT echo getLine

构建返回单子动作的单子动作,然后手动组合内部动作,在大多数情况下会忽略单子转换器的全部意义。与其有两层单子动作,不如有一个单层,在内部动作之上有一个外部动作的转换器版本——也就是说,而不是使用ReaderT r Foo (IO a) 动作,这需要手动绑定两者ReaderT r Foo 层和 IO 层,您应该使用 ReaderT r (FooT IO) a 操作,其中只有一个绑定同时处理读取器、foo 和 IO 效果。

【讨论】:

  • 哇,这正是我需要的!太感谢了!如果可以的话,我会给你两次投票。
【解决方案2】:

使用join。它具有类型签名

join :: Monad m => m (m a) -> m a

擅长

join :: IO (IO ()) -> IO ()

您可以使用hoogle 找出答案。它是一个命令行工具。我们可以按类型签名搜索:

hoogle "IO (IO ()) -> IO ()"

给予

Control.Monad join :: Monad m => m (m a) -> m a
Control.Composition (.$) :: Monad m => m (m a) -> m a
RIO join :: Monad m => m (m a) -> m a
Universum.Monad.Reexport join :: Monad m => m (m a) -> m a
Stack.Prelude join :: Monad m => m (m a) -> m a
Relude.Monad.Reexport join :: Monad m => m (m a) -> m a
Intro join :: Monad m => m (m a) -> m a
Hledger.Web.Import join :: Monad m => m (m a) -> m a
Data.Edison.Seq concat :: Sequence s => s (s a) -> s a
Data.Edison.Seq.Defaults concatUsingFoldr :: Sequence s => s (s a) -> s a
-- plus more results not shown, pass --count=20 to see more

它有几个正是你想要的功能。

【讨论】:

  • Hoogle 也有一个网站:haskell.org/hoogle(或 hoogle.haskell.org 以获得最新的 alpha 版本)。您还可以使用集成到 Stackage 中的 Hoogle 搜索,可通过 stackage.org 的搜索栏访问。最后,如果您在查找某些内容时遇到问题,还可以使用 Hayoo,这是 Hoogle 的替代品。
猜你喜欢
  • 2012-01-27
  • 2019-05-22
  • 2012-04-19
  • 2018-05-24
  • 1970-01-01
  • 2019-03-28
  • 1970-01-01
  • 2014-05-07
  • 1970-01-01
相关资源
最近更新 更多