【问题标题】:IORef in HaskellHaskell 中的 IORef
【发布时间】:2019-02-27 07:03:09
【问题描述】:

我想知道 Haskell 中 IORef 的用法是否合法?更具体地说,如果有人可以解决以下问题或指出适当的地方以了解更多信息,我将不胜感激:

  1. 使用 IORef 是否被认为是一种糟糕的 Haskell 实践?如果是,为什么?更具体地说,它比 IO monad 好还是坏?
  2. 如果想为程序添加状态,状态单子不是更好(更纯粹)的方法吗?如果一个人感觉更有必要,难道他还不能使用 STM 和 MVar,而且还能过得更好吗?

  3. 是否存在使用 IORef 而不是 STM、MVar 或纯 IO 可以轻松处理的编程场景?

我正在阅读一篇将 IORef 用于代码 sn-ps 的论文,由于我对 IORef 的负面看法,这对我来说很难阅读。与其沉溺于我的无知,我认为向我的 Haskeller 同伴寻求帮助可能是一个更好的主意。

【问题讨论】:

  • Related,虽然我不会称其为 dup。
  • 我可能只在涉及并发的情况下使用 IORef。是的,使用 IORef 来编写命令式代码会很糟糕。 MVar 和 STM 变量很棒,但如果 IORef 满足您的需求,那么使用它并没有错。
  • 我猜论文是并发的?如上所述,我还没有真正在并发应用程序之外看到过 IORef。
  • @AJFarmar 这篇论文是关于在软件安全领域使用类型理论的。

标签: haskell monads ioref


【解决方案1】:

首先,我认为对于论文中的代码 sn-ps,使用 IORef 是完全明智的,特别是如果论文不是关于可变引用或并发的最佳实践。 IORef 易于理解,具有直接的语法和语义(尤其是在非并发设置中),如果您希望读者专注于 IORefs 以外的示例方面,这是一个自然的选择。不幸的是,作者的方法适得其反——忽略IORefs 并注意论文的其余部分。

(如果论文关于可变引用或并发的最佳实践,那么它可能是在更好的替代方案出现之前编写的。)

无论如何,对于您的更大问题,使用IORef 的主要反对意见是:

  • 与在程序中引入可变状态的任何其他机制一样,它使您的代码更难以推理、维护、测试等——函数式编程支持者所说的所有常见事情都会使 FP 获得“优势”突变密集型命令式算法。
  • 它只是一个围绕专用STRef RealWorld 的新类型包装器,它在STRef 上添加的唯一内容是一些原子操作。在非并发代码中,没有充分的理由不在ST s monad 中使用STRef s 值,因为它们更灵活——您可以使用runST 在纯代码中运行它们,或者,如果需要,在带有 stToIO 的 IO monad。
  • 在并发代码中,有更强大的抽象,例如 MVarSTM,它们比 IORefs 更容易使用。

因此,如果可变状态是“坏”的,而且——如果你真的需要它——更好的替代方案取决于你是否需要并发,没有太多可推荐的IORef

另一方面,如果您已经在处理 IO monad 中的一些非并发代码,因为您需要执行实际的 IO 操作,并且您确实需要一些普遍的可变状态很难从 IO 中解脱出来,那么使用IORefs 似乎是合法的。

关于您更具体的问题:

  1. 我想可以肯定地说,当使用weaker tool would do the job 时,使用IORef 被认为是“不好的做法”。那个较弱的工具可能是STRef s,或者更好的是State monad,或者更好的是一个完全不需要任何状态的重写的高阶算法。因为IORef 将 IO 与可变引用结合在一起,所以它是一种命令式大锤,可能会导致最单一的 Haskell 代码,因此最好避免使用它,除非它“显然”是针对特定问题的正确解决方案。

  2. State monad 通常是向程序添加状态的首选惯用方式,但它通过在计算中线程化一系列不可变状态值来提供可变状态的“幻觉”,而不是全部算法可以通过这种方式有效地实现。如果需要真正的可变状态,STRef 通常是非并发设置中的自然选择。请注意,您可能不会在非并发设置中使用 MVarSTM - 在这种情况下没有理由使用它们,即使您没有使用它们,它们也会迫使您进入 IO monad否则需要它。

  3. 是的,在某些编程场景中,IORefSTRef 优于 StateSTMMVar 或纯 IO(见下文)。在少数情况下IORef 显然比STRef 更可取,但是——如上所述——如果你已经在IO monad 中并且需要与 IO 操作纠缠在一起的真正可变状态,那么IORef 的语法可能比 STRef 更简洁。

IORefSTRef 是一种好方法的一些示例:

  • base 包中的 Data.Unique 使用 IORef 作为生成唯一对象的全局计数器。
  • base 库中,文件句柄内部广泛使用IORefs 将缓冲区附加到句柄。这是“已经在 IO monad 中进行纠缠的 IO 操作”的一个很好的例子。
  • 许多向量算法是使用可变向量最有效地实现的(例如,甚至像计算数据块中的字节频率这样简单的方法)。如果您使用 vector 包中的可变向量,那么从技术上讲,您使用的是可变字节数组,而不是 STRefIORef,但它在道德上仍然是等效的。
  • equivalence 包使用STRefs 来高效实现联合连接算法。
  • 举个简单的例子,如果您要为命令式语言实现解释器,那么对可变变量使用 IORefSTRef 值通常是最有效的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-24
    • 1970-01-01
    • 1970-01-01
    • 2018-05-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多