[进行共享实验时,我发现预定义的const
函数在某些情况下的行为有所不同。
f :: (() -> Int) -> Int
f g = g () + g ()
x1 = f (const (trace "x1" 42))
x2 = f (\_ -> (trace "x2" 42))
x3 = f (myconst (trace "x3" 42))
myconst :: a -> b -> a
myconst x _ = x
this example的编译没有优化时,评估x1
仅触发一次trace
评估,而针对x2
和x3
评估两次。由于lambda函数,这对于x2
是合理的。
x1
x2
x2
x3
x3
252
definition的const
暗示,这是一个普通的函数定义,没有任何编译器注释可以解释差异。因此,函数myconst
的行为应与否相同。如何解释这种行为,并在这方面有办法影响编译器?
如果myconst
被编译成一个单独的模块(即使该单独的模块是用-O0
编译的,则输出为:
x1
x2
x2
x3
252
不同之处在于,在-O0
代码中,在单独的模块中调用myconst
会生成代码:
let x' = myconst (trace "x3" 42) in x' + x'
但是在同一模块中调用myconst
内联如下:
trace "x4" 42 + trace "x4" 42
用-O2
编译会完全更改代码-内联所有内容,并且将跟踪移到表达式的顶部,因此它们仅执行一次。
您可以在这方面明显地影响编译器,例如,可以通过将myconst
放在或不放在单独的模块中,或者-如@leftroundabout所指出的那样-通过添加各种内联编译指示。
我认为您不能可靠地在这方面影响编译器,而且我不确定您可以通过研究未优化的编译输出了解多少有关实际GHC代码的知识。我认为以上示例清楚地表明,基于-O0
生成的代码将基于完全不需要的编译方面,以完全任意和自相矛盾的方式运行。