它显示ghci
在下面全部是懒惰的,
ghci > let x = trace "1" 1 in x + x
1
1
2
编译后运行以下代码,strictness analyzer增强了默认ghc
。
main = do print $ let x = trace "1" 1 in x + x
1
2
但即使将-O0 -fno-strictness
选项传递给ghc
,为什么结果仍然相同?
答案 0 :(得分:7)
这与严格性无关。如果你考虑一下,GHCi的行为实际上是愚蠢的:它“计算”x
两次。懒惰与否,常数不应该被计算两次!
发生了什么:x
的通用数字类型为Num a => a
。这意味着,它的实现并不仅仅是一个常量,而是一个带有“类型”的函数 - (实际上是一个字典 - )参数。函数通常不可能进行记忆,因此只要需要其值,就会重新计算这样的多态值 。这很烦人,因此标准Haskell通过一个有争议的措施monomorphism restriction来避免这种情况。它基本上消除了多态性,如果它可以因此将值转换为constant applicative forms。因此,使用更简单的类型x
来推断Integer
,其中是然后是一个常量,它只在计算一次时触发trace
而第二次评估只是重新计算使用已知的值。 (这是“适当的懒惰”行为!)
你在GHCi中没有看到这个的原因是从版本7.8开始,它默认关闭了单态限制!您可以将其打开以查看其功能:
GHCi, version 7.10.2: http://www.haskell.org/ghc/ :? for help
Prelude> :m +Debug.Trace
Prelude Debug.Trace> let x = trace "1" 1 in x + x
1
1
2
Prelude Debug.Trace> :set -XMonomorphismRestriction
Prelude Debug.Trace> let x = trace "1" 1 in x + x
1
2
幸运的是,在实际程序中,即使将单态性限制设置为 off ,也不会发生双重评估,因为当在函数体中定义x
时,编译器可以看到在函数的范围内,它不是多态的。因此,您的第二个代码永远不应该跟踪1
两次。 (尽管总是记住,trace
只是一个粗略的调试工具,它真的可以对付语言的粒度 - 通常不会指望它有任何可重现的行为。)