[英]How to flatten IO (IO ())?
我只是在學習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請求,以便我可以在我的測試中將其替換為模擬實現。
從根本上說,這個想法是monad變換器堆棧,其中一個變換器為您提供IO
無論是HTTP請求還是getLine
。 我是在考慮這個錯誤還是有辦法做到這一點?
使用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
它有幾個功能,正是你想要的。
問題的答案是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
構建返回monadic動作的monadic動作,然后手動組合內部動作,在大多數情況下忽略了monad變換器的全部要點。 不應該有兩層monadic動作,你應該有一個單層,它在內部動作之上有一個外部動作的變換器版本 - 也就是說,而不是使用ReaderT r Foo (IO a)
動作,這需要手動綁定ReaderT r Foo
層和IO
層,你應該使用ReaderT r (FooT IO) a
動作,其中只有一個綁定一次處理reader,foo和IO效果。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.