You have a
while loop, or a
do while loop that mutates an initial value by some algorithm.
Replace the loop with a fold, passing the initial value and algorithm as arguments.
Loops generally coincide with mutable state, which is not referentially transparent.
Folds not only eliminate mutation, but they're more concise. Less code means fewer opportunities for typos and logic errors.
When encountering a loop over a foldable data structure, note what state it's computing with each iteration, and what the initial state is:
Here, our loop accumulates a sum of the list values. The initial state of the sum is zero.
Rewrite the loop body as a function taking a tuple of the prior value of the computed state and the current value of the foldable data structure, and returning the next value of the computed stated.
Fold over the original data structure, passing the initial state value and the rewritten loop body function.
Here, we use a left fold:
foldLeft: List[A] => B => (((B, A) => B) => B)
Not all loops are over a foldable data structure like
List. Sometimes we just loop until some arbitrary condition is false:
Here, we count the number of loop iterations for ten iterations.
In this case, we just need to fold over a data structure of the right length. We don't care about its values.
In this example, we can use a
Not all loops reduce a bunch of values into a single result. Sometimes the result is another bunch of values.
Here, we create a new list containing the squares of the numbers in the old list.
This time, we use a right fold:
foldRight: List[A] => B => (((A, B) => B) => B)
If we had used a left fold, our result would be backwards:
sum: 10 fold sum: 10 count: 10 fold count: 10 squares: List(1, 4, 9, 16) fold squares: List(1, 4, 9, 16) fold squares reverse: List(16, 9, 4, 1)