我一直在努力了解Haskell中共享计算是如何工作的。根据我的理解,无分共享计算应该只评估一次(向CSE提供)。
(A)例如,考虑下列代码及其输出:
*Main> let foo = trace "eval foo" 5 in foo + foo
eval foo
10
*Main> let foo' = \x -> trace "eval foo'" x in (foo' 5) + (foo' 5)
eval foo'
eval foo'
10正如预期的那样,foo只进行一次评估(CSE可能会启动),而foo'则被惰性地计算两次。这很好。我使用GHCi 7.6.3版本进行了上述操作。接下来,我在GHCi版本8.6.5中尝试了相同的代码,得到了这个结果:
*Main> let foo = trace "eval foo" 5 in foo + foo
eval foo
eval foo
10注意,现在对foo进行了两次评估。
(B)与GHCi类似,7.6.3版:
*Main> let goo = const (trace "eval goo" 5) in goo () + goo ()
eval goo
10但是,GHCi 8.6.5版对goo进行了两次评估:
*Main> let goo = const (trace "eval goo" 5) in goo () + goo ()
eval goo
eval goo
10(C)最后,这两种版本的结果如下:
*Main> let foo_wrapper x = let foo = trace "eval foo" x in foo + foo
*Main> foo_wrapper 5
eval foo
10我想知道GHCi-8中是否默认关闭了一些优化,还是trace强制foo的副作用以某种方式进行了两次评估?还是GHCi-7有问题?GHCi应该如何处理(A)和(B)这样的表达式?
(更新1)
对于场景(C),请考虑GHCi-8中的以下运行(trace的第二个参数的主要区别):
*Main> let foo_wrapper x = let foo = trace "eval foo" x in foo + foo
*Main> foo_wrapper 5
eval foo
10
*Main> :t (foo_wrapper 5)
(foo_wrapper 5) :: Num a => a
*Main> let foo_wrapper' x = let foo = trace "eval foo" 5 in foo + foo
*Main> foo_wrapper' ()
eval foo
eval foo
10
*Main> :t (foo_wrapper' ())
(foo_wrapper' ()) :: Num a => a发布于 2019-09-04 14:43:51
正如预期的那样,只对
foo进行一次评估(CSE可能会启动)
不,这与CSE无关,它只是一个懒惰的评估(也就是按需要调用)的工作方式:foo是一个常量应用形式,因此它只需要计算一次(从一次被强制计算到WHNF),然后就可以被重用而不需要任何进一步的计算。这在GHCi-8中不再有效的原因是7.8已经删除了单态约束中的GHCi。为什么这是相关的?trace "eval foo" 5是Num a => a类型的多态表达式。多态表达式不可能是CAF。因此,不必按需要调用,而是按名称调用语义。
再次获得共享的最简单方法是通过添加显式签名,使类型为单形,从而强制执行CAF:
Prelude Debug.Trace> let foo = trace "eval foo" 5 :: Int in foo + foo
eval foo
10https://stackoverflow.com/questions/57789663
复制相似问题