如何展平IO(IO())? [英] How to flatten IO (IO ())?

查看:95
本文介绍了如何展平IO(IO())?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习Haskell和monad转换器,我发现自己想使用IO(IO)来扩展为IO().我确定我做错了什么,但无法确切指出我迷路的地方.

I'm just learning Haskell and monad transformers and I've found myself with an IO (IO ()) that I'd like to flatten into just IO (). I'm sure that I'm doing something wrong, but can't pinpoint exactly where I'm getting lost.

这是我要执行的操作的简化示例.这是实现echo的复杂方法,但它说明了问题.

Here is a simplified example of what I'm trying to do. This is a convoluted way of implementing echo, but it illustrates the problem.

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请求,以便在测试中将其替换为模拟实现.

In my real application, I have a Spock app that makes HTTP requests to an upstream server. Spock apps use a monad transformer stack called SpockCtxT and I'd like to insert a ReaderT into the stack to abstract the HTTP request so that I can swap it out for a mock implementation in my tests.

从根本上讲,这个想法是一个monad转换器堆栈,其中一个转换器为您提供IO,无论是HTTP请求还是getLine.我是在不正确地考虑这个问题还是有某种方法可以做到这一点?

Fundamentally, the idea is a monad transformer stack where one of the transformers gives you an IO whether it be an HTTP request or getLine. Am I thinking about this incorrectly or is there some way to do this?

推荐答案

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

The answer to the question as asked is join :: IO (IO ()) -> IO (). But the answer to the question I think you should have asked is liftIO :: IO () -> ReaderT (IO String) IO (). Like this:

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效果.

Building monadic actions that return monadic actions, and then manually combining the inner actions, is in most cases ignoring the whole point of monad transformers. Instead of having two layers of monadic actions, you should have a single layer which has a transformer version of the outer action on top of the inner action -- that is, instead of working with ReaderT r Foo (IO a) actions, which require manual binds for both the ReaderT r Foo layer and the IO layer, you should be working with ReaderT r (FooT IO) a actions, where just one binding handles the reader, foo, and IO effects at once.

这篇关于如何展平IO(IO())?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆