liveBook · Manning

Foldable

As explained in Functional Programming in Scala, Second Edition

A foldable data type is a data type whose elements can be traversed and reduced to a result by a folding operation.

A Foldable is a typeclass for type constructors whose elements can be reduced to one result. It abstracts over the container: summing integers should not depend on whether they live in a List, Vector-like IndexedSeq, LazyList, tree, or Option. Its core operations are foldRight, foldLeft, and foldMap; combineAll reduces elements directly when their element type has a Monoid. section 10.6 section 10.9

trait Foldable[F[_]]:
  extension [A](as: F[A])
    def foldRight[B](acc: B)(f: (A, B) => B): B
    def foldLeft[B](acc: B)(f: (B, A) => B): B
    def foldMap[B](f: A => B)(using m: Monoid[B]): B
    def combineAll(using m: Monoid[A]): A =
      as.foldLeft(m.empty)(m.combine)

The interface separates a structure F[_] from its element type A. A fold supplies an accumulator or maps each A into a monoidal result, so the same reduction vocabulary works across shapes. section 10.6

The operations can be derived from one another. foldLeft and foldRight can be expressed through foldMap using monoids of endofunctions, while foldMap can be expressed through foldRight and a result Monoid. An implementation must override at least one fundamental operation, however, or mutually derived defaults recurse forever. It may also override derived methods when the underlying structure has a more efficient implementation. section 10.9

Useful instances make the abstraction concrete:

  • List and LazyList delegate left and right folds to their underlying operations. section 10.9
  • IndexedSeq can override foldMap with divide-and-conquer foldMapV for more efficient evaluation. section 10.9
  • Tree folds recursively through branches; Option returns the accumulator or monoid empty value when absent. section 10.9
  • toList is derivable with foldRight, though a List instance can return itself directly. section 10.9

Reducing a tree or collecting a bag

A Tree instance combines the folded left and right branches with the target Monoid. The same foldMap pattern can turn an IndexedSeq into a frequency map by mapping every element to a one-entry map and combining those maps. The reduction logic stays independent of the input shape. section 10.9 section 10.9

Foldable only promises that elements can be visited and reduced; it does not promise that the original shape can be rebuilt. Therefore a Foldable need not be a Functor. Traverse is stronger: it extends both Functor and Foldable, and can define foldMap by traversing with a constant applicative built from a Monoid. section 12.7.1 section 12.9

How Functional Programming in Scala, Second Edition covers it

How deep it goes
Derives fold operations, compares implementation choices, and implements instances for List, IndexedSeq, LazyList, Tree, and Option.
Examples
Uses integer summation, List, IndexedSeq, LazyList, Tree, Option, conversion to List, and frequency-map construction.
What it assumes
Uses Scala typeclasses, context parameters, type constructors, and Monoids while defining Foldable operations concretely.
What you can do after
Build it from scratch by defining and optimizing Foldable instances and deriving reusable reductions.
How it connects
Connects folds to Monoids, higher-kinded type constructors, Functor, Applicative, and Traverse.

Also explained in

Compare how 2 books explain foldable →

Related concepts

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.