直觉上我的回答是否定的,是的。但是,让我通过尝试来回答您的问题。考虑这段代码:
import Debug.Trace
expensive :: Int -> Int
expensive x = trace ("expensive evaluated for " ++ show x) $ x
{-# NOINLINE expensive #-}
cheap :: Int -> Int
cheap x = x
{-# NOINLINE cheap #-}
foobar x y = expensive x + cheap y
foobar' x = let k = expensive x in \ y -> k + cheap y
part f = sum [f i| i<-[0..10]]
main = do
print $ part (foobar 5)
print $ part (foobar' 5)
如果我们运行它,结果是
$ ./Test
expensive evaluated for 5
110
expensive evaluated for 5
110
所以编译器足够聪明,也可以优化原始版本。但为什么?因为他内联了foobar
in的定义main
,然后注意到它可以将表达式浮动到expensive 5
对 的调用之外part
。如果我们禁用foobar
and的内联foobar'
(或者不使用-O
),它就不再起作用了:
$ ./Test
expensive evaluated for 5
expensive evaluated for 5
expensive evaluated for 5
expensive evaluated for 5
expensive evaluated for 5
expensive evaluated for 5
expensive evaluated for 5
expensive evaluated for 5
expensive evaluated for 5
expensive evaluated for 5
expensive evaluated for 5
110
expensive evaluated for 5
110
因此,虽然 GHC 在某些情况下可以做正确的事情,但如果您想依赖它,您应该始终检查是否是这种情况。要么使用类似的工具Debug.Trace
,要么通过查看核心 ( -ddump-simpl
)。