我希望将 Haskell 用于包含不断变化的重状态的实时应用程序。
当然,状态是不可变的,因此在每个状态步骤中,我都会重新创建一个稍作更改的新状态并丢弃旧状态。在这种情况下,结果会非常低效,因为我不需要以前的状态。
我经常遇到人们说 GHC 可以优化这类东西并在内部改变不可变的值,我想确保它会这样做。
可能吗?有没有办法确定 GHC 是否会通过内部改变值来优化它?有没有办法强制执行/确保它会执行?
附注此优化有正式名称吗?
GHC 本身不这样做。各种容器库使用一种称为“流融合”的技巧,这意味着纯功能代码建议的一些副本实际上从未制作过——但这仍然不是真正的“内部突变”,而是它将多个操作组合在一起,每个操作将涉及到一个大操作的副本,但仍然只有一个副本。 我认为以全自动的方式获得真正的“突变优化”是不太可行的;有些语言,比如
Mercury声称他们可以做到这一点,但我真的不知道它的效果如何。 然而,像 Haskell 这样的优秀纯函数式语言完全能够“显式”处理可变状态:通过 monad。这可以是“全能”的
IO
monad(有点令人皱眉,因为你失去了所有的 ref-transp. 保证,但对于实时应用程序来说,这可能是正确的事情),或者是专门的 ST
monad,其目的是专门允许您使用真正的可变状态,同时保持程序的外部行为纯功能性。 如果您采用这种方法,您不仅可以确保
确保不会制作昂贵的副本,而且您最终可能会得到更好的代码。因为有时候突变正是思考某些问题的正确方式;即使是真正纯功能的代码,如果您在
State
monad 中“假装”使用可变状态,有时也会变得更好。
AFAIK ghc
。
但它的运行时针对这种“状态略有改变”的情况进行了优化。通常你的状态是(或者可以表示为)类似树的东西,并且大多数 mange 实际上重用了大部分现有的树。所以修改只需要操作很少的指针,而且效率很高。考虑这个例子:data State = State
{ theA :: A
, theB :: B
}
data A = A Int
data B = B String
modifyTheA :: (A -> A) -> State -> State
modifyTheA f s = s {theA = f (theA s)}
这里
modifyTheA
函数创建了新的
State
,但它只是两个指针。整个
theB
字符串被重复使用。GHC 在一个条件下进行此优化:如果一个对象在声明它的函数中使用一次并且没有创建对其的其他引用。如果对象是在单独的函数中创建和使用的,则不会应用它,除非源函数是内联的。