Why is it sometimes possible to fold an infinite list from the right?

Your interleave is insufficiently lazy. The magic thing that right folds must do to work on infinite structures is to not inspect the result of the folded value too closely before they do the first bit of computation. So:

interleave x stream = Cons x $ case stream of
    Cons y ys -> Cons y (interleave x ys)

This produces Cons x _ before inspecting stream; in contrast, your version requires stream to be evaluated a bit before it can pass to the right hand side of the equation, which essentially forces the entire fold to happen before any constructor gets produced.

You can also see this in your list version of interleave:

interleave x list = [x] ++ intersperse x list ++ [x]

The first element of the returned list (x) is known before intersperse starts pattern matching on list.


We can inspect the source code of foldr [src]. A less noisy version looks like:

foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)

Haskell does not evaluate eagerly. This thus means that, unless you need (foldr f z xs), it will not evaluate the accumulator. This thus means that f does not need the second parameter, for example because the first item x has a certain value, it will not evaluate the accumulator.

For example if we implement takeWhileNeq:

takeWhileNeq a = foldr f []
    where f x xs -> if x == a then [] else (x:xs)

if we thus run this on a list takeWhileNeq 2 [1,4,2,5], then it will not evaluate anything. If we however want to print the result it will evaluate this as:

   f 1 (foldr f [4,2,5])

and f will inspect if 1 == 2, since that is not the case, it will return (x:xs), so:

-> 1 : foldr f [4,2,5]

so now it will evaluate 4 == 2, and because this is false, it will evaluate this to:

-> 1 : (4 : foldr f [2,5])

now we evaluate 2 == 2, and since this is True, the function returns the empty list, and ingores the accumulator, so it will never look at foldr f [5]:

-> 1 : (4 : [])

For an infinite list, it will thus also result an empty list and ignore folding the rest of the list.