【问题标题】:Are there valid reasons to build monad transformer stacks on top of IO?是否有正当理由在 IO 之上构建 monad 转换器堆栈?
【发布时间】:2018-09-29 13:13:37
【问题描述】:

IO 在 Haskell 中很棘手。线程、FFI、异步异常、惰性,应有尽有。

然后我们有MonadIO,它允许在底部使用IO 分层一元堆栈。既然IO 动作可以做任意事情,那么在这种摇摇欲坠的基础上构建一元堆栈有什么价值?

它为什么存在?如果您确实需要执行任意副作用,为什么不构建自定义 monad IO

【问题讨论】:

  • 但这就是我们所做的......? MonadIO is just a typeclass 将所有此类“自定义单子”组合在一起。碰巧很多有用的 monad 可以表示为 monad 转换器堆栈。你确定你清楚你在这里问什么?
  • 假设我给你我的库,它做了一些有用的事情,但都在MonadIO。您会使用它而不用担心库写入文件、使用网络或分叉线程吗?你会在不检查源代码的情况下使用它的更新版本吗?IO 操作实际上做了什么?
  • 我真的不明白为什么它被标记为一个坏问题。
  • @AJFarmar sevo 似乎在问为什么我们不在各处使用某种细粒度的效果系统。参照。 Delimiting the IO monadchrispenner.ca/posts/monadio-considered-harmful 等建议

标签: haskell io monads


【解决方案1】:

现在使用the ReaderT design pattern 是一种常见的方法。

您对在 IO 之上放置其他转换器保持警惕是正确的,上面链接的博客文章解释了其中一些原因。

但是,当将“通用应用配置”传递给您的所有函数时,ReaderT 提供了一个不错的小便利。

【讨论】:

    【解决方案2】:

    在这种不稳定的基础上构建一元堆栈的价值是什么 基础?

    IO 之上的变形金刚可能让您更方便地谈论重复动作的序列(因此variousstreaminglibraries)。

    他们还可以帮助进行externally allocated resources 所需的簿记。

    IO 的“野性”并不普遍反对在其上堆叠转换器,因为它们可以帮助您避免重复代码并使基本逻辑更清晰。

    相反,argumentIO 已经提供了一些用于处理错误(exceptions)和可变引用(IORefs、MVars...)的内置功能,因此为此添加了转换器已经存在的功能可能有点矫枉过正。

    可变引用的一个论点是,由“纯”维护的状态意味着当异常弹出时消失,这可能不是您想要的。您还可以从多个线程访问可变引用。

    【讨论】:

      【解决方案3】:

      MonadIO 在为 不是 MonadIO 实例的类型实现函数时很有用。

      newtype FooMonad a
        = FooMonad (StateT Int IO a)
        deriving (Functor, Applicative, Monad)
      
      doFoo :: FooMonad String
      doFoo = FooMonad $ liftIO getLine
      

      您可以使用StateT IntIOMonadIO 实例方便地定义“原始”FooMonad 操作。使用您的模块的其他人仅限于您选择导出的原语。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-27
        • 2017-01-09
        • 2017-12-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多