Недавно я читаю это сообщение в блоге Майклом Снояном. В упражнении, предложенном там, я попытался определить $!
оператор сам:
import Prelude hiding ( ($!) )
($!) :: (a -> b) -> a -> b
($!) f x = x 'seq' f x
mysum :: [Int] -> Int
mysum list0 =
go list0 0
where
go [] total = total
go (x:xs) total = go xs $! total + x
main = print $ mysum [1..1000000]
Я думал, что это работает хорошо, хотя использование памяти было ужасным. Мой первый вопрос: это. Почему это не помогло?
Затем я проверил его определение в Prelude. В нем говорится:
($!) :: (a -> b) -> a -> b
f $! x = let !vx = x in f vx -- see #2273
Итак, я скопировал его в свой код:
{-# LANGUAGE BangPatterns #-}
import Prelude hiding ( ($!) )
($!) :: (a -> b) -> a -> b
($!) f x =
let !vx = x
in f vx
mysum :: [Int] -> Int
mysum list0 =
go list0 0
where
go [] total = total
go (x:xs) total = go xs $! total + x
main = print $ mysum [1..1000000]
и результат был следующим:
Linking mysum4 ...
500000500000
209,344,064 bytes allocated in the heap
130,602,696 bytes copied during GC
54,339,936 bytes maximum residency (8 sample(s))
66,624 bytes maximum slop
80 MB total memory in use (0 MB lost due to fragmentation)
Вы можете видеть, насколько ужасно это сравнивается с результатом использования Prelude $!
оператор:
Linking mysum4 ...
500000500000
152,051,776 bytes allocated in the heap
41,752 bytes copied during GC
44,384 bytes maximum residency (2 sample(s))
21,152 bytes maximum slop
1 MB total memory in use (0 MB lost due to fragmentation)
Мой второй вопрос - откуда это произошло?
Более того, я думаю, что он может быть переписан следующим образом:
($!) :: (a -> b) -> a -> b
f $! !x = f x
Есть ли причина не делать этого? Это мой третий вопрос.