A logical thread is an independent line of computation that can be created freely for a problem and later mapped onto scarce operating-system threads. It describes concurrency without committing the program to a physical thread. section 7.1.1
Direct Thread and Runnable APIs make results depend on side effects and expose physical-thread management. Future and ExecutorService abstract over those threads, but blocking get still offers too little compositional structure. A functional parallelism API instead represents computation as a value such as Par[A], allowing parallel work to be combined before execution. section 7.1.1
Logical threads matter when:
- Each element of a collection should receive its own concurrent computation rather than sharing one filtering thread. section 7.9
- Nested fork operations create more logical work than the executor has physical threads, potentially leaving every worker blocked while another logical thread waits to run. section 7.9
Parallel filtering
A sequential implementation lifts as.filter(f) with unit or lazyUnit, but the predicate calls still share one logical thread. Mapping asyncF over the input gives each predicate invocation its own Par computation; sequence then combines the results and flatten removes rejected elements. section 7.9
def parFilter[A](as: List[A])(f: A => Boolean): Par[List[A]] =
fork: #1
val pars: List[Par[List[A]]] =
as.map(asyncF(a =>
if f(a) then List(a) else Nil)) #2
sequence(pars).map(_.flatten) #3
The implementation makes logical-thread granularity visible: fork starts the overall computation, asyncF creates one computation per element, and sequence composes them. section 7.9


