首先,我认为对于论文中的代码片段,使用IORef是非常明智的,特别是如果论文不是关于可变引用或并发的最佳实践时。 IORef易于理解,具有直接的语法和语义(尤其是在非并发环境中),如果您希望读者专注于示例的其他方面而不是IORefs. 不幸的是,作者的方法对你适得其反——忽略IORefs 并注意论文的其余部分。
(如果这篇论文是关于可变引用或并发的最佳实践,那么它可能是在更好的替代方案出现之前编写的。)
无论如何,对于您更大的问题,使用的主要反对意见IORef是:
- 与将可变状态引入程序的任何其他机制一样,它使您的代码更难以推理、维护、测试等——函数式编程支持者所说的所有常见事物都使 FP 比突变密集型具有“优势”命令式算法。
- 它只是一个围绕着 special 的新类型包装器
STRef RealWorld,它唯一添加的STRef就是一些原子操作。在非并发代码中,没有充分的理由不在 monad 中使用STRef s值ST s,因为它们更灵活——您可以在纯代码中运行它们,runST或者如果需要,在 IO monad 中使用stToIO.
- 在并发代码中,有更强大的抽象,比如
MVar和STM比IORefs 更容易使用。
因此,就可变状态是“坏的”而言,并且——如果你真的需要它——更好的替代方案取决于你是否需要并发,没有什么可推荐的IORef。
另一方面,如果您已经在处理 monad 中的一些非并发代码,IO因为您需要执行实际的 IO 操作,并且您确实需要一些不易与 IO 分离的普遍可变状态,那么使用IORefs似乎合法。
关于您更具体的问题:
我想可以肯定地说,当一个较弱的工具可以完成这项工作IORef时,使用被认为是“不好的做法” 。那个较弱的工具可能是一个,或者更好的是一个monad,或者更好的是一个重写的高阶算法,它根本不需要任何状态。因为将 IO 与可变引用相结合,所以它是一种命令式大锤,可能会导致最单一的 Haskell 代码,因此最好避免使用它,除非它“显然”是特定问题的正确解决方案。STRef sStateIORef
monad 通常是向程序添加状态的State首选惯用方式,但它通过将一系列不可变状态值通过计算线程化来提供可变状态的“幻觉”,并且并非所有算法都可以通过这种方式有效地实现。在需要真正的可变状态时,STRef通常是非并发设置中的自然选择。请注意,您可能不会使用MVar或STM在非并发设置中使用它们——在这种情况下没有理由使用它们,IO即使您不需要它,它们也会强迫您进入 monad。
是的,在某些编程场景中,IORef或STRef优于State、STM、MVar或 pure IO(见下文)。很少有场景IORef显然STRef比.更简洁的语法。IOIORefSTRef
IORef或者STRef是一个好方法的一些例子:
Data.Unique在base包中使用一个IORef作为全局计数器来生成唯一的对象。
- 在
base库中,文件句柄内部广泛使用IORefs 将缓冲区附加到句柄。这是“已经在 IO monad 中进行纠缠的 IO 操作”的一个很好的例子。
- 许多向量算法使用可变向量最有效地实现(例如,甚至像计算数据块中的字节频率一样简单)。如果您使用
vector包中的可变向量,那么从技术上讲,您使用的是可变字节数组而不是STRefor IORef,但它在道德上仍然是等价的。
- 该
equivalence包使用STRefs 来有效实现联合连接算法。
- 作为一个有点直接的例子,如果你正在为命令式语言实现解释器,那么使用可变变量的
IOReforSTRef值通常是最有效的。