【问题标题】:Can IO action in negative position give unexpected results?负位置的 IO 动作会产生意想不到的结果吗?
【发布时间】:2019-01-26 14:23:27
【问题描述】:

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

引用斯诺伊曼1

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

还有凯米特2

我倾向于导出带有 MonadIO 约束的函数...无论何时 不必在负面位置采取类似 IO 的动作(作为 论据)。

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

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

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

【问题讨论】:

  • 没有危险。就是做不到。 Possible duplicate.
  • 什么不能做? IO a 处于负面位置?我可能会错误地措辞这个问题。我赞成这个答案,因为它对我有所帮助,但我无法理解。
  • 不,将具有在否定位置提及 IO 的类型的术语概括为适用于任何 MonadIO 实例的类型是无法做到的。
  • 但我不想一概而论。我想保持IO a 处于负面地位,我在问为什么它似乎不受欢迎。
  • 对此没什么气馁的。你误解了评论。

标签: haskell io-monad


【解决方案1】:

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

没有危险。恰恰相反,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

【讨论】:

  • 假设我不想概括并保留IO a。到目前为止,它似乎可以做MonadIO 版本不能做的事情。为什么这很重要?
  • @sevo 我不确定我是否理解。 “它似乎可以做 MonadIO 版本不能做的事情”听起来你正在与 被概括的东西进行比较。你有更具体的例子吗?
  • @sevo 我想我现在明白了:不鼓励在否定位置使用IO a。 Snoyman 和 Kmett 都关心编写在 monad 转换器上工作的代码,因此他们关心能够编写函数的通用版本,如果没有比 MonadIO 更强大的抽象,这实际上是不可能的。
  • 有道理!但!我们是因为语言缺陷还是因为它是正确的做法而选择不那么普遍的抽象? Snoyman 似乎支持后者。
  • @sevo 两者兼而有之。这是Snoyman laying it outMonadBaseControl 使用类型族和多参数类型类。 MonadIO 是普通的 Haskell98。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-18
相关资源
最近更新 更多