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