Compositionality means combining components into larger computations without inspecting their internal implementations. In parallel programming, this requires meaningful values and operations that compose rather than side effects or blocking control flow. section 7.1.1
Concurrency APIs differ in how well they support generic composition:
- Runnable returns Unit, so extracting results requires side effects and knowledge of internal behavior. section 7.1.1
- Future abstracts physical threads but exposes blocking get and no compositional API for combining computations. section 7.1.1
- A Par data type represents parallel computations as values, allowing larger computations to be assembled from smaller ones. section 7.1.1
Listing 7.2 Updating sum with our custom data type
def sum(ints: IndexedSeq[Int]): Int =
if ints.size <= 1 then
ints.headOption.getOrElse(0)
else
val (l, r) = ints.splitAt(ints.size / 2)
val sumL: Par[Int] = Par.unit(sum(l)) #1
val sumR: Par[Int] = Par.unit(sum(r)) #2
Par.get(sumL) + Par.get(sumR) #3
The recursive sum example shows composition concretely: split the input, wrap each subcomputation with Par.unit, and retrieve their results with Par.get. section 7.1.1

