处于负面位置的IO操作能否带来意外结果?

时间:2019-01-26 14:23:28

标签: haskell io-monad

关于Monad IOIO之间的区别似乎有一些未记录的知识。注释herehere)暗示IO a可以在否定位置使用,但可能会产生意想不到的后果:

引用Snoyman 1

  

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

还有Kmett 2

  

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

     

当我的代码确实必须采取另一项单调操作作为参数时,   然后我通常必须停下来思考一下。

程序员应该了解的此类功能中是否存在危险?

例如,这是否意味着运行任意基于持续性的操作可能会重新定义控制流,从而使基于Monad IO的界面可以安全地获得意外结果?

1 个答案:

答案 0 :(得分:3)

  

程序员应该了解的此类功能中是否存在危险?

没有危险。相反,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。您不能(至少不能仅使用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