[英]Why isn't Haskell able to optimize this? (Nothing gets propagated needlessly in the Maybe monad.)
讓我們開始吧
boom :: Int -> Maybe a -> Maybe a
boom 0 x = x
boom n x = boom (n-1) (x >>= (\y -> Just y))
這是一個簡單的函數,只是反復將(或>>=
)一個Maybe
值推入一個簡單的\\y -> Just y
函數。
現在,該計划
main = do
let z = boom 10 (Nothing :: Maybe Int)
putStrLn $ show z
一瞬間跑得很快。 但是,該計划
main = do
let z = boom 10000000 (Nothing :: Maybe Int)
putStrLn $ show z
即使我使用ghc -O
(GHC 7.8.3)進行編譯,也需要幾秒鍾才能完成。
這意味着Haskell無法優化此功能。 即使沒有必要,也Nothing
被反復推入函數中。
我的問題是, 為什么 ? 為什么不能推斷出Nothing
都不會總是因為反復推遲而Nothing
? 換句話說, 為什么是不是能夠在第一立即短路 Nothing
?
你的函數是一個很好的例子,它很慢, 因為它是尾遞歸的 。 在嚴格的語言中,尾遞歸函數通常是首選,因為它們通常會帶來更好的性能(在時間和空間上)。 在懶惰的尾部遞歸並不是那么有益。 實際上,函數的非尾遞歸變體是:
boom :: Int -> Maybe a -> Maybe a
boom 0 x = x
boom n x = x >>= (\y -> boom (n-1) (Just y))
當x
是一個Just something
時,上面仍會循環n
次。 但是,它會在常量空間中執行此操作,而不像在第二個參數中構建大型thunk的原始代碼。 更好的是,當x
為Nothing
,上面的代碼將立即返回。
我確實意識到這並沒有真正回答你關於“為什么”GHC無法對此進行優化的問題。 但希望,它可以表明這些優化非常微妙,並且通常涉及歸納推理。 期望編譯器優化它可能要求有點過多。
你需要使用-fforce-recomp
強制重新編譯
用ghc
編譯得到1.44s
用ghc -O
編譯得到1.44s
用ghc -O -fforce-recomp
並得到0.00s到0.04s
注意
它仍然無法使用boom maxBound (Nothing :: Maybe Int)
你會等很長時間。
因為動boom
擴大了n-1
。
所以boom 100 x
= boom (100-1) ...
= boom (100-1-1) ...
等等......
你可以嘗試交換繁榮的論點(但我不確定這會改變什么)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.