在进行共享实验时,我发现预定义的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
但是,
const
的definition表示这是一个普通的函数定义,没有任何编译器注释可以解释差异。因此,功能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
生成的代码将以完全无关紧要的方式运行,基于您可能不会想到的编译的次要方面。