Определенные вами примеры кода генерируют идентичный скомпилированный код. Если вы возьмете следующую программу:
{-# 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
. Например, следующие два действия ввода-вывода различны:
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 ()
, что объясняет их различное поведение.