Why does foldr work in an infinite list?
This function can work on infinity join lists, and it's easy to see why:
findKey :: (Eq k) => k -> [(k,v)] -> Maybe v
findKey key [] = Nothing
findKey key ((k,v):xs) = if key == k
then Just v
else findKey key xs
When it finds the key, it returns Just v
, stopping the recursion. Now consider the following implementation:
findKey' :: (Eq k) => k -> [(k,v)] -> Maybe v
findKey' key = foldr (\(k,v) acc -> if key == k then Just v else acc) Nothing
How does the compiler / interpreter know that when a key matches k, it can return it?
*Main> findKey' 1 $ zip [1..] [1..]
returns Just 1
When he finds that key == k
, he returns Just v
. Why does recursion stop there, allowing us to do things like this with infinity association lists?
Since the function passed to foldr
does not always evaluate the parameter acc
, that is, it is lenin in that parameter.
For example,
(\(k,v) acc -> if 1 == k then Just v else acc) (1,"one") (error "here be dragons!")
will return "one"
without even trying to evaluate the expression error
.
Moreover, foldr
by definition it satisfies:
foldr f a (x:xs) = f x (foldr f a xs)
If it is x:xs
infinite, but f
does not use its second argument, it foldr
can return immediately.
In your example, f
evaluates its second element if and only if the first argument is not a necessary association. This means that the list of associations will only be evaluated enough to find the association key
.
If you like experimenting, try this instead:
foldr (\(k,v) acc -> case acc of
Nothing -> if key == k then Just v else acc
Just y -> if key == k then Just v else acc) Nothing
case
looks redundant as the function returns the same on both branches. However, this requires evaluation acc
, breaking the code into infinite lists.
One more thing you can try
foldr (:) [] [0..]
This basically restores the infinite list as it is.
foldr (\x xs -> x*10 : xs) [] [0..]
This multiplies everything by 10
and is equivalent map (*10) [0..]
.
A non-empty case foldr
can be defined as foldr f init (x:xs) = f x (foldr f init xs)
. In your case it f
is (\(k,v) acc -> if key == k then Just v else acc)
, so it (k,v)
denotes the current item in the list, and acc
denotes (foldr f init xs)
. That is, it acc
means a recursive call. In then
-case you are not using acc
, so no recursive call occurs, since Haskell is lazy, value arguments are not evaluated before (and if not used).