直覺我的回答會是沒有和是。但讓我通過試用來回答你的問題。考慮以下代碼:如果我們運行
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
所以編譯器很聰明,優化原來的版本也是如此。但爲什麼?因爲他在main
中內嵌了foobar
的定義,因此會注意到它可能會將expensive 5
中的表達式浮出呼叫part
。如果我們禁用內聯爲foobar
和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
)。
這不會改變語義,'map(foo 5)[]; foo a b = undefined a + b'如果您強制評估地圖上的'foo 5',您將不必要地炸掉 – jozefg
@jozefg'let'不會強制評估。它是在第一次需要時進行評估(然後希望共享所有進一步的用途)。 –