IORef in Haskell



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

  1. 使用IORef是否被认为是一种糟糕的Haskell做法?如果是,为什么?更具体地说,它如何比IO monad更好或更糟
  2. 如果一个人想把状态添加到程序中,那么状态monad不是更好(更纯粹)的方法吗?如果一个人觉得更有必要,他就不能继续使用STM和MVar吗?

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

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

首先,我认为对于论文中的代码片段,使用IORef是非常明智的,特别是如果论文不是关于可变引用或并发的最佳实践的。IORef易于理解,具有直接的语法和语义(尤其是在非并发环境中),如果你想让读者专注于你的例子中除了IORefs之外的方面,这是一个自然的选择。不幸的是,作者的方法对你产生了反效果——只需忽略IORefs,并关注论文的其余部分。

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

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

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

因此,在可变状态"糟糕"的程度上——如果你真的需要它——根据你是否需要并发性,可以提供更好的替代方案,IORef没有太多值得推荐的地方。

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

关于您更具体的问题:

  1. 我想可以肯定地说,使用IORef被认为是"糟糕的做法",而较弱的工具可以完成这项工作。这个较弱的工具可能是STRef s,或者更好的是Statemonad,或者更好地是一个完全不需要任何状态的重写的高阶算法。因为IORef将IO与可变引用结合在一起,所以它有点像一把命令式的大锤,很可能会导致最独特的Haskell代码,所以最好避免它,除非它"显然"是解决特定问题的正确方案。

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

  3. 是的,在某些编程场景中,IORefSTRefStateSTMMVar或纯IO更可取(见下文)。在少数情况下,IORef显然比STRef更可取,但如上所述,如果您已经处于IOmonad中,并且需要与IO操作纠缠在一起的真正可变状态,那么IORef在稍微干净的语法方面可能比STRef更具优势。

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

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

    • 没有找到相关文章

    最新更新