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.
foldlis objectively worse thanfoldl', for instance, despite it being possible to implement both tail recursively in a straightforward way. - Silvio Mayolo