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?
source to share
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..]
.
source to share
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).
source to share