liveBook · Manning

Parallelism

As explained in Functional Programming in Scala, Second Edition

Parallelism is the execution of multiple parts of a computation at the same time, typically on multiple processor cores or CPUs.

Parallelism structures a computation so independent parts may run at the same time. Multiple cores and CPUs make this valuable, but shared mutable memory makes thread-based programs difficult to reason about: races, deadlocks, weak testability, and poor scaling can follow. A functional approach describes parallel work with pure values, then runs that description separately. section 7.0 section 7.5

Start with a computation whose independent structure is visible. A sequential left fold sums a sequence in one chain. Splitting the sequence in half, recursively summing both halves, and combining the answers exposes two subcomputations that can proceed in parallel. The small example matters because it isolates the design problem before incidental application detail obscures it. section 7.1

Describe parallel summation before running it

Instead of directly creating operating-system threads, represent a result-producing parallel computation as Par[A]. A leaf becomes Par.unit(value); recursive halves remain Par[Int]; and map2 combines their results. Returning Par[Int], rather than immediately extracting each result with get, preserves the computation as a composable value and lets map2 express that its two inputs are independent. section 7.1.1 section 7.1.2

def sum(ints: IndexedSeq[Int]): Par[Int] =
  if ints.size <= 1 then
    Par.unit(ints.headOption.getOrElse(0))
  else
    val (l, r) = ints.splitAt(ints.size / 2)
    Par.map2(sum(l), sum(r))(_ + _)

This divide-and-conquer sum returns a Par[Int]: unit describes leaf work, while map2 combines two independent recursive descriptions without forcing their results early. section 7.1.2

The API should separate logical parallelism from execution policy. Direct Runnable and Thread APIs communicate results through effects and tie work to scarce operating-system threads. By contrast, Par values can be created freely, transformed, and combined; a separate run interpreter later maps the description onto execution resources. Strictness also matters: if map2 evaluates arguments strictly, Scala’s left-to-right evaluation constructs the entire left summation tree before the right, undermining the intended symmetry. section 7.1.1 section 7.1.2 section 7.5

Useful design rules and generalizations include:

  • Treat fork as meaning-preserving: fork(x) should produce the same result as x while describing asynchronous execution in a separate logical thread. section 7.3.2
  • Generalize a Boolean choice between two computations to choiceN, choiceMap, chooser, and ultimately flatMap or join when the next computation depends on a prior result. section 7.4
  • Use associative monoid combination in a balanced fold: independent inner combines can run simultaneously, and balanced trees can reduce costly intermediate allocations. section 10.3

Parallelism here is an algebraic design problem as well as an execution problem. Laws constrain implementations, a small primitive vocabulary supports derived combinators, and familiar operations such as map, flatMap, and sequence make parallel computations reusable. Non-blocking implementation can use actors internally while retaining a purely functional public API. section 7.6

How Functional Programming in Scala, Second Edition covers it

How deep it goes
Derives Par, map2 strictness, laws, and generalized combinators, while deferring execution behind a separate interpreter.
Examples
Uses divide-and-conquer integer summation, conditional computation choice, and balanced string concatenation folds.
What it assumes
Builds from concrete Scala sequence summation while using higher-order functions and recursive divide-and-conquer structure.
What you can do after
Build it from scratch as a composable Par library with combinators, laws, and a separate execution interpreter.
How it connects
Connects parallelism to pure functions, map2, fork, flatMap, monoids, balanced folds, and actors.

Also explained in

Compare how 38 books explain parallelism →

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.