parFoldMap maps every element of an IndexedSeq and reduces the mapped results as parallel computations. It combines parallelism with a Monoid, whose associative operation permits results to be grouped by halves without changing the final answer. section 10.9 section 10.3
The implementation separates the two stages:
- Use parMap to transform the input sequence, producing a Par[IndexedSeq[B]]. section 10.9
- Reduce the mapped sequence with foldMapV, using a Monoid lifted to Par[B]. section 10.9
- Define par so combine applies the original monoid operation through Par.map2 and empty lifts the original identity with Par.unit. section 10.9
Implementation shape
def par[A](m: Monoid[A]): Monoid[Par[A]] = new: def combine(a1: Par[A], a2: Par[A]) = a1.map2(a2)(m.combine) val empty = Par.unit(m.empty) def parFoldMap[A, B](as: IndexedSeq[A], m: Monoid[B])(f: A => B): Par[B] = Par.parMap(as)(f).flatMap(bs => foldMapV(bs, par(m))(b => Par.lazyUnit(b)) ) section 10.9
The design works because the reduction is associative: a sequence may be combined left-to-right, by halves, or through another grouping while preserving the result. The same foldMapV structure can therefore support sequential and parallel reductions, with different efficiency characteristics. section 10.3 section 10.9
import fpinscala.parallelism.Nonblocking.* def par[A](m: Monoid[A]): Monoid[Par[A]] def parFoldMap[A, B](as: IndexedSeq[A], m: Monoid[B])(f: A => B): Par[B]
Use the compact signatures to compare the Monoid lifting step with the final parallel fold-map operation. section 10.3
