liveBook · Manning

Fold

As explained in Functional Programming in Scala, Second Edition

A fold is an operation that combines a sequence of outputs into one accumulated value using an associative operation and an initial value.

fold evaluates a pull incrementally until it terminates, applying an accumulator function to each emitted value. It returns both the final result and the accumulated state. section 15.2

Accumulating a pull

A pull can be folded with an initial accumulator and a function such as (total, value) => total + value. Each output becomes the next accumulator, while the pull's terminating result is preserved separately. section 15.2

Use fold when stream consumption should produce a controlled result rather than retain every output.

  • Collect outputs with toList when retaining them is appropriate. section 15.3.1
  • Discard outputs while executing effects by folding with unit: fold(())((_, _) => ()). section 15.3.1
  • Manage scopes by stepping pulls, tracking the active scope, and closing the root scope after completion or failure. section 15.3.3
Listing 15.2 The Pull data type
enum Pull[+O, +R]:
  case Result[+R](result: R) extends Pull[Nothing, R]
  case Output[+O](value: O) extends Pull[O, Unit]
  case FlatMap[X, +O, +R](
    source: Pull[O, X],
    f: X => Pull[O, R]) extends Pull[O, R]
 
  def step: Either[R, (O, Pull[O, R])] = this match            #1
    case Result(r) => Left(r)
    case Output(o) => Right(o, Pull.done)
    case FlatMap(source, f) =>
      source match
        case FlatMap(s2, g) =>
          s2.flatMap(x => g(x).flatMap(y => f(y))).step
        case other => other.step match
          case Left(r) => f(r).step
          case Right((hd, tl)) => Right((hd, tl.flatMap(f)))
 
  @annotation.tailrec
  final def fold[A](init: A)(f: (A, O) => A): (R, A) =         #2
    step match
      case Left(r) => (r, init)
      case Right((hd, tl)) => tl.fold(f(init, hd))(f)
 
  def toList: List[O] =                                        #3
    fold(List.newBuilder[O])((bldr, o) => bldr += o)(1).result
 
  def flatMap[O2 >: O, R2](
    f: R => Pull[O2, R2]
  ): Pull[O2, R2] =                                            #4
    Pull.FlatMap(this, f)
 
  def >>[O2 >: O, R2](next: => Pull[O2, R2]): Pull[O2, R2] =
    flatMap(_ => next)
 
  def map[R2](f: R => R2]): Pull[O, R2] =
    flatMap(r => Result(f(r)))

Inspect the Pull representation and its tail-recursive fold implementation. section 15.2

How Functional Programming in Scala, Second Edition covers it

How deep it goes
Defines the recursive accumulator loop and extends it to effectful execution and resource-scope management.
Examples
Uses Pull accumulation, effectful logging, output draining, and scope-safe file and resource processing.
What it assumes
Assumes familiarity with Pull, step, flatMap, streams, and effect types.
What you can do after
Apply fold to accumulate outputs, discard them during effects, or run streams while preserving cleanup behavior.
How it connects
Connects Pull, step, flatMap, streams, effects, resource safety, and toList.

This explainer was generated by Manning's knowledge graph from the text of Functional Programming in Scala, Second Edition. The section links point to the parts of the book it draws on.