64

一直让我感到困惑的一件事是现在是否是使用 IORef 的好时机。在决定是否将 IORef 用于任务时,是否应遵循任何准则?什么时候是在 IORef 上使用 State monad 的好时机?

4

4 回答 4

74

State 和它的相关 ST 都产生可以作为单元运行的“单片”有状态计算。他们基本上将可变状态视为产生结果所需的中间数据,但其本身不应引起程序的其余部分的兴趣。

另一方面,放置在 IORef 中的不是要运行的“计算”——它只是一个包含简单值的框,可以在 IO 中以相当任意的方式使用。这个盒子可以放在数据结构中,在程序的(IO部分)周围传递,在方便的时候替换它的内容,被一个函数封闭等等。事实上,变量的很多杂乱性质和像 C 这样的语言的指针可以用 IORefs 建模,为任何希望维护他/她能够用任何语言编写 C 代码的声誉的 C 专家程序员提供极大的帮助......这绝对是要小心使用的东西。

尽管如此,有时在单个代码块中隔离与一个可变状态的所有交互是非常笨拙的,如果不是完全不可能的话——一些状态必须简单地传递,放入数据结构等。在这样的情况下在这种情况下,盒子方法可能是唯一的选择。在 48 小时内编写自己的方案教程(强烈推荐,顺便说一句)介绍可变状态的章节提供了一个示例。(有关为什么使用 IORefs 而不是 State 或 ST 来在特定的 Scheme 解释器设计中对 Scheme 环境进行建模的真正最合适的讨论,请参阅链接。)

简而言之,这些环境需要以任意方式嵌套,在用户交互实例之间进行维护((define x 1)在 Scheme REPL 中键入应该可能导致用户稍后能够键入x并取回 1 作为值),放入对象中建模 Scheme 函数(因为 Scheme 函数关闭了它们创建的环境)等。

总而言之,我想说如果一项任务看起来非常适合它,State 将倾向于提供最干净的解决方案。如果需要多个单独的状态,也许 ST 可以提供帮助。但是,如果有状态计算难以使用或无法锁定在自己的代码段中,则状态需要在复杂程序的大部分生命周期中以可修改的形式持续存在等,那么 IORefs 可能只是合适的东西。

再说一次,如果需要那种可以通过 IO 代码以受控方式传递和交互的可变状态,为什么不检查一下 STM 及其 TVar!它们在存在并发的情况下要好得多,事实上,以至于解决一些与并发相关的任务实际上很简单。不过,这与问题并没有真正的关系,所以我会拒绝详细说明。:-)

于 2009-12-19T04:40:44.737 回答
15

唔。当您需要一些可变状态但处于单线程环境中时,您将使用 IORef。或者,当您想要一个更大的结构中的可变字段又由同步变量保存时。

通常,使用 MVar。它们具有更强大的语义。

于 2009-12-19T04:15:21.073 回答
3

就个人而言,我会说IORef且仅当您已经在使用 s 时才使用 s 是可以的IO。否则,总是State,除非您需要ST. 可以在 monad 中使用多个状态线程State,以及一些辅助函数——您只需将状态设为元组或记录,并定义函数来分别设置、获取或更新每个字段。

特别是,使用StateT s IO. 如果你已经在 中IO,那么你已经有了可变状态,所以你不妨使用它——ReaderT (IORef s) IO例如。

于 2011-11-27T21:52:45.190 回答
1

STRef在状态本地化并且不需要与环境交互时使用。

于 2009-12-19T04:14:17.223 回答