hi guys, I completely don’t understand what’s a benefit to use $! instead of $ in foldr’ implementation from GHC.List. Here are my thoughts:
Here is foldr’ from GHC.List
foldr' :: (a -> b -> b) -> b -> [a] -> b
foldr' f a xs = foldl (\ g x -> \ acc -> g $! f a x) id xs 0
And here is simple example
foldr' (+) 0 [1..3]
I will rewrite inner guts of foldl in terms of classic example, not foldr, for simplicity
foldl :: (b -> a -> b) -> b -> [a] -> b
foldl f a [] = a
foldl f a (x:xs) = foldl f (f a x) xs
I’m replacing x with elements from the list
f (f (f id 1) 2) 3
I’m starting “unfold” the full expression to see what will be the final one
-- f id 1 = (\ g x -> \ acc -> g $! f a x) id 1
f (f (\ acc -> id $! 1 + acc) 2) 3
-- f (\ acc -> id $! 1 + acc) 2 = (\ acc -> (\ acc -> id $! 1 + acc) $! 2 + acc)
f (\ acc -> (\ acc -> id $! 1 + acc) $! 2 + acc) 3
-- f (\ acc -> (\ acc -> id $! 1 + acc) $! 2 + acc) 3 =
-- (\ acc -> (\ acc -> (\ acc -> id $! 1 + acc) $! 2 + acc) $! 3 + acc)
(\ acc -> (\ acc -> (\ acc -> id $! 1 + acc) $! 2 + acc) $! 3 + acc) 0
Now the question: $! evaluates the second argument and passes it to the function, but here we don’t know when acc will appear, so we have to postpone evaluation, we are creating thunk. So why $! instead of $ here? If we finally get 0, it will be evaluated in any case with or without $! and then $ or $! will pass the result to another lambda and so on and so forth. So where is strict in foldr then?
\ acc -> id $! 3 + acc
-- for me they are equivalent
\ acc -> id $ 3 + acc
I believe that I’ve missed something but I don’t know what exactly - could you help me, please?
as you can the latter expression builds thunks and passing it further, at the end it will evaluate, however in this case in the heap will be a lot of thunks