首页 > 解决方案 > 负位置的 IO 动作会产生意想不到的结果吗?

问题描述

Monad IO关于和之间的区别似乎有一些无证知识IO此处此处的备注) 提示IO a可用于否定位置,但可能会产生意想不到的后果:

引用 Snoyman 1

但是,我们知道一些控制流(例如异常处理)没有被使用,因为它们与 MonadIO 不兼容。(原因:MonadIO 要求 IO 处于正位置,而不是负位置。)这让我们知道,例如, foo 在 ContT 或 Conduit 等基于延续的 monad 中使用是安全的。

和Kmett 2

我倾向于导出具有 MonadIO 约束的函数......只要它不必在负位置(作为参数)采取类似 IO 的动作。

当我的代码确实必须将另一个单子动作作为参数时,我通常不得不停下来考虑一下。

程序员应该知道的这些功能是否存在危险?

例如,这是否意味着运行任意基于延续的操作可能会重新定义控制流,从而以Monad IO基于接口安全的方式给出意想不到的结果?

标签: haskellio-monad

解决方案


程序员应该知道的这些功能是否存在危险?

没有危险。恰恰相反,Snoyman 和 Kmett 的观点是,Monad IO不要让你IO以消极的积极态度度过难关。

假设你想概括putStrLn :: String -> IO (). 你可以,因为IO它处于积极的位置:

putStrLn' :: MonadIO m => String -> m ()
putStrLn' str = liftIO (putStrLn str)

现在,假设您想概括handle :: Exception e => (e -> IO a) -> IO a -> IO a. 你不能(至少不能用 just MonadIO):

handle' :: (MonadIO m, Exception e) => (e -> m a) -> m a -> m a
handle' handler act = liftIO (handle (handler . unliftIO) (unliftIO act))

unliftIO :: MonadIO m => m a -> IO a
unliftIO = error "MonadIO isn't powerful enough to make this implementable!"

你需要更多的东西。如果您对如何做到这一点感到好奇,请查看lifted-base. 例如:handle :: (MonadBaseControl IO m, Exception e) => (e -> m a) -> m a -> m a


推荐阅读