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.