11

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

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

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

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

4

1 回答 1

12

首先,我认为对于论文中的代码片段,使用IORef是非常明智的,特别是如果论文不是关于可变引用或并发的最佳实践时。 IORef易于理解,具有直接的语法和语义(尤其是在非并发环境中),如果您希望读者专注于示例的其他方面而不是IORefs. 不幸的是,作者的方法对你适得其反——忽略IORefs 并注意论文的其余部分。

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

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

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

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

另一方面,如果您已经在处理 monad 中的一些非并发代码,IO因为您需要执行实际的 IO 操作,并且您确实需要一些不易与 IO 分离的普遍可变状态,那么使用IORefs似乎合法。

关于您更具体的问题:

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

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

  3. 是的,在某些编程场景中,IORefSTRef优于StateSTMMVar或 pure IO(见下文)。很少有场景IORef显然STRef比.更简洁的语法。IOIORefSTRef

IORef或者STRef是一个好方法的一些例子:

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