In a lazy language like Haskell, tail recursion is often a bad idea. This is one of these cases.
We might attempt to make foldr
tail recursive by e.g. starting with a reverse
(which is also doable in a tail-recursive way), and then accumulating the foldr
result step by step, in a tail recursive way. However, that would break the foldr
semantics.
With the standard foldr
, we have e.g.
foldr (\_ _ -> k1) k2 (x:xs) = k1
no matter what xs
is, including a bottom value like undefined
or an infinite list like [0..]
. Further, when xs
is a finite but long list, the code above is also efficient, since it stops the computation immediately without scanning the whole list.
As a more practical example,
and :: [Bool] -> Bool
and = foldr (&&) True
makes and xs
return False
as soon as some element of xs
evaluates to False
, without scanning the rest of the list.
Concluding, turning foldr
into a tail recursive function would:
- change the semantics when dealing with partially-defined lists (
1:2:undefined
) or infinite ones ([0..]
);
- be less efficient on finite length lists, which would always have to be completely scanned, even when there is no need to.