哈斯克尔的IORef

use*_*465 6 monads haskell ioref

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

  1. 使用IORef被认为是一个糟糕的Haskell实践吗?如果是,为什么?更具体地说,它比IO monad更好还是更差?
  2. 如果有人想要在程序中添加状态,那么状态monad不是更好(更纯粹)的方式.如果一个人感觉更加迫切,他还不能继续使用STM和MVar,还能更好吗?

  3. 是否有使用IORef而不是STM,MVar或纯IO轻松处理的编程方案?

我正在阅读一篇使用IORef作为代码片段的论文,由于我对IORef的负面看法,我很难读到这篇论文.我不想沉迷于我的无知,而是想让我的同伴Haskellers寻求帮助可能是一个更好的主意.

K. *_*uhr 7

首先,我认为对于论文中的代码片段,使用IORef是非常明智的,特别是如果论文不是关于可变引用或并发的最佳实践. IORef很容易理解,具有直接的语法和语义(特别是在非并发设置中),如果您希望读者专注于除了IORefs 之外的示例的各个方面,这是一个自然的选择.令人遗憾的是,作者的方法已经适合你了 - 只要忽略它IORef并注意论文的其余内容.

(如果该论文关于可变引用或并发性的最佳实践,也许它是在更好的替代方案可用之前编写的.)

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

  • 像任何其他将可变状态引入程序的机制一样,它会使你的代码更难以推理,维护,测试等等. - 函数式编程支持者所说的所有常见事情都会让FP在突变密集型方面具有"优势"命令式算法.
  • 它只是一个专门的新类型包装器STRef RealWorld,它唯一添加的STRef是一些原子操作.在非并发代码中,没有充分的理由不在monad中使用STRef sST s,因为它们更灵活 - 您可以在纯代码中运行它们runST,如果需要,可以在IO monad中运行它们stToIO.
  • 在并行代码中,有更强大的抽象,像MVarSTM,更容易比上班IORef秒.

所以,在可变状态是"坏"的情况下 - 如果你真的需要它 - 根据你是否需要并发性,可以使用更好的替代方案,没有太多建议IORef.

另一方面,如果你已经在IOmonad中处理一些非并发代码,因为你需要执行实际的IO操作,并且你真的需要一些不容易从IO解开的普遍可变状态,那么使用IORefs看似合法.

关于您更具体的问题:

  1. 我想可以肯定地说,IORef当一个较弱的工具能够完成工作时,使用被认为是"不好的做法" .那个较弱的工具可能是一个STRef s或更好的Statemonad或更好的重写高阶算法,根本不需要任何状态.因为IORefIO与可变引用相结合,所以它是一种必要的大锤,可能会导致最单一的Haskell代码成为可能,所以最好避免它,除非它"显然"是针对特定问题的正确解决方案.

  2. State单子通常是添加状态到程序的优选的惯用方式,但它通过计算线程不可变状态值的序列提供了一个可变状态的"幻象",并且不是所有的算法可以高效地实现这种方式.在需要真正的可变状态的情况下,a STRef通常是非并发设置中的自然选择.请注意,您可能不会使用MVarSTM在非并发设置中 - 在这种情况下没有理由使用它们,IO即使您不需要它也会强制您进入monad.

  3. 是的,有被编程场景,其中任一IORefSTRef优于State,STM,MVar,或纯IO(见下文).很少有情况IORef显然是优选的STRef,但是 - 如上所述 - 如果你已经在IOmonad中并且需要与IO操作纠缠在一起的真正可变状态,那么IORef可能STRef在略微方面有优势清洁语法.

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

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