我期望以下代码段:
main = do
let !x = [2,3,5,2,3,5,6,7,1,3,0,1]
begin <- getCPUTime
let !rx = reverse x
end <- getCPUTime
putStrLn $ "Calculation time: " ++ show (end - begin) ++ " ps."
putStrLn $ "Result: " ++ show rx
与以下版本相同:
main = do
let x = [2,3,5,2,3,5,6,7,1,3,0,1]
begin <- x `seq` getCPUTime
let rx = reverse x
end <- rx `seq` getCPUTime
putStrLn $ "Calculation time: " ++ show (end - begin) ++ " ps."
putStrLn $ "Result: " ++ show rx
这是真的吗?如果在第一个版本中,x
和rx
在“需要”时评估为WHNF,则为false。
顺便说一句,我想提出一种用于深度评估的语法糖,名为“双爆炸模式”。
您提供的特定代码示例将生成相同的编译代码。如果您采用以下程序:
{-# LANGUAGE BangPatterns #-}
module Bang where
import System.CPUTime
main1 = do
let !x = [2,3,5,2,3,5,6,7,1,3,0,1]
begin <- getCPUTime
let !rx = reverse x
end <- getCPUTime
putStrLn $ "Calculation time: " ++ show (end - begin) ++ " ps."
putStrLn $ "Result: " ++ show rx
main2 = do
let x = [2,3,5,2,3,5,6,7,1,3,0,1]
begin <- x `seq` getCPUTime
let rx = reverse x
end <- rx `seq` getCPUTime
putStrLn $ "Calculation time: " ++ show (end - begin) ++ " ps."
putStrLn $ "Result: " ++ show rx
并使用(GHC版本8.6.5)进行编译:
stack ghc -- -dsuppress-all -dsuppress-uniques -ddump-simpl -fforce-recomp -O2 Bang.hs
您将在转储的GHC内核中发现main1
和main2
被编译为完全相同的代码,实际上它们被拉出到单独的main4
函数中:
main1
main1 = main4 `cast` <Co:3>
main2
main2 = main4 `cast` <Co:3>
HOWEVER,通常,let !x = ...
构造与使用let x = ...
后接x `seq` y
并不完全等效。例如,以下两个IO操作是不同的:
foo :: IO ()
foo = do
let !x = undefined
return ()
bar :: IO ()
bar = do
let x = undefined
return $ x `seq` ()
第一个立即产生异常:
main = do
print 1
foo -- EXCEPTION!
print 2
第二个命令在执行时不执行任何操作,但如果您尝试检查其结果,则会产生异常:
main = do
print 1
bar -- does nothing
print 2
x <- bar -- also does nothing
print 3
() <- bar -- EXCEPTION!
print 4
[我相信,退避三舍之后,bar
和foo
等效于:
bar = return (undefined `seq` ())
foo = undefined `seq` return ()
这说明了他们的不同行为。