You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: source_md/higher-order-functions.md
+53-3Lines changed: 53 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -673,9 +673,59 @@ It would be `map' f xs = foldl (\acc x -> acc ++ [f x]) [] xs`, but the thing is
673
673
If you reverse a list, you can do a right fold on it just like you would have done a left fold and vice versa.
674
674
Sometimes you don't even have to do that.
675
675
The `sum` function can be implemented pretty much the same with a left and right fold.
676
-
One big difference is that right folds work on infinite lists, whereas left ones don't!
677
-
To put it plainly, if you take an infinite list at some point and you fold it up from the right, you'll eventually reach the beginning of the list.
678
-
However, if you take an infinite list at a point and you try to fold it up from the left, you'll never reach an end!
676
+
*One big difference is that right folds work on infinite lists, whereas left ones don't!*
677
+
678
+
How `foldr` works with infinite lists
679
+
680
+
Recall the definition of `foldr`:
681
+
```{.haskell:hs}
682
+
foldr f acc [] = acc
683
+
foldr f acc (x:xs) = f x (foldr f acc xs)
684
+
```
685
+
The key insight is that the recursive call `foldr f acc xs` is passed as an argument to the function `f`. Because Haskell is lazy, if `f` never uses its second argument, the recursive call is never evaluated. This allows `foldr` to terminate on infinite lists, provided `f` is lazy in its right argument.
686
+
687
+
Example: Checking for a Value in an infinite list
688
+
689
+
Suppose we want to check if `3` appears in an infinite list `[1..]`. We can use `foldr` like this:
690
+
```{.haskell:hs}
691
+
containsThree :: [Int] -> Bool
692
+
containsThree = foldr (\x rest -> if x == 3 then True else rest) False
693
+
```
694
+
Let's unfold this step-by-step on the infinite list `[1,2,3,4,...]`:
695
+
1. Start: foldr (...) False [1,2,3,4,...]
696
+
2. For `x=1`: `if 1 == 3 then True else (foldr (...) False [2,3,4,...])` -> `1 /= 3`, so this becomes `foldr (...) False [2,3,4,...]` (we recurse).
697
+
3. For `x=2`: `if 2 == 3 then True else (foldr (...) False [3,4,5,...])` -> `2 /= 3`, so this becomes `foldr (...) False [3,4,5,...]` (we recurse again).
698
+
4. For `x=3`: `if 3 == 3 then True else (foldr (...) False [4,5,6,...])` -> `3 == 3` is `True`! *Here, `f` ignores the recursive call (the [4,5,6,...] part) and returns True immediately*.
699
+
700
+
Why `foldl` Fails with Infinite Lists
701
+
702
+
Now, contrast this with foldl's definition:
703
+
```{.haskell:hs}
704
+
foldl f acc [] = acc
705
+
foldl f acc (x:xs) = foldl f (f acc x) xs
706
+
```
707
+
Notice that `foldl` always makes a recursive call first, passing `f acc x` as the new accumulator. This means:
708
+
Even if the accumulator becomes True at some point, foldl will still continue recursing down the list.
709
+
For an infinite list, this leads to infinite recursion (it never stops).
710
+
711
+
Example: `foldl` on the Same Problem
712
+
713
+
If we try to implement containsThree with `foldl`:
714
+
```{.haskell:hs}
715
+
containsThreeFoldl :: [Int] -> Bool
716
+
containsThreeFoldl = foldl (\acc x -> if x == 3 then True else acc) False
5. Compute `f False 3` -> `True`. But now we must recurse: `foldl (...) True [4,5,6,...]`.
723
+
6. Compute `f True 4` -> `True` (since 4 /= 3, it returns the accumulator True). But we recurse again: `foldl (...) True [5,6,7,...]`.
724
+
7. This continues forever. Even though we found 3, we never stop.
725
+
726
+
`foldr` can terminate on infinite lists if the function `f` is lazy in its right argument (i.e., `f` can short-circuit by ignoring the recursive result).
727
+
`foldl` always processes the list from left to right and cannot short-circuit in the same way, so it fails on infinite lists.
728
+
This is why `foldr` is often more suitable for working with infinite lists. Just remember: *the function `f` must be lazy in its right argument for `foldr` to terminate early*.
679
729
680
730
**Folds can be used to implement any function where you traverse a list once, element by element, and then return something based on that.
681
731
Whenever you want to traverse a list to return something, chances are you want a fold.**
0 commit comments