Let's repeat your definitions and add a couple more:
(&&)::Bool->Bool->Bool
True && x = x
False && _ = False
and :: [Bool]->Bool
and xs = foldr (&&) True xs
repeat :: a -> [a]
repeat a = a : repeat a
foldr :: (a -> r -> r) -> r -> [a] -> r
foldr _ z [] = z
foldr f z (a:as) = f a (foldr f z as)
Now, we can prove this by evaluating it manually, taking care to do it "lazily" (outermost applications first, and evaluating only enough to resolve the outermost data constructors):
and (repeat False)
= foldr (&&) True (repeat False) -- definition of `and`
= foldr (&&) True (False : repeat False) -- definition of `repeat`
= False && foldr (&&) True (repeat False) -- `foldr`, second equation
= False -- `&&`, second equation
The key is that the second equation of the definition of &&
discards its second argument:
False && _ = False
This means, in runtime terms, that we never force foldr
's recursive call at a step where we encounter False
.
Another way to look at it is to contemplate foldr
's second equation, and what it means when we have lazy evaluation:
foldr f z (a:as) = f a (foldr f z as)
Since the recursive call to foldr
happens as an argument to f
, this means that, at runtime, the function f
decides whether the value of its second argument is necessary or not, and so chooses at each step of the fold whether to recurse down the list or not. And this "decision" process proceeds from left to right.
In my understanding foldr will loop through the list from tail till head. Is there any internal "break" mechanism?
Strictly speaking, in a pure functional language there is no intrinsic notion of evaluation order. Expressions may be evaluated in any order that's consistent with their data dependencies.
What you've said here is a common misunderstanding that people who've learned foldr
from impure, eager languages carry over to Haskell. In an eager language that's a useful rule of thumb, but in Haskell, with purity lazy evaluation, that rule will only confuse you. Often the opposite rule of thumb is useful when programming Haskell: foldr
will visit the list elements from left to right, and at each step its f
argument function gets to decide whether the rest of the list is necessary.
The extreme example of this is to implement a function to get the head of a list using foldr
:
-- | Return `Just` the first element of the list, or `Nothing` if the
-- list is empty.
safeHead :: [a] -> Maybe a
safeHead = foldr (\a _ -> Just a) Nothing
So for example:
safeHead [1..]
= foldr (\a _ -> Just a) Nothing [1..]
= foldr (\a _ -> Just a) Nothing (1:[2..])
= (\a _ -> Just a) 1 (foldr (\a _ -> Just a) Nothing [2..])
= Just 1