liveBook · Manning

Flatmap

As explained in Functional Programming in Scala, Second Edition

flatMap is a monadic combinator that applies a computation-producing function and combines its result with the surrounding monadic context.

flatMap chains a value-producing computation with a function that chooses the next computation from that value. Unlike map, whose function returns one plain result, flatMap accepts a function returning a result already in the surrounding context and combines that result without leaving an extra layer behind. section 3.3.3 section 4.6

Expanding a list

For List, the function can return any number of outputs per input. Applying i => List(i, i) to List(1, 2, 3) produces List(1, 1, 2, 2, 3, 3): each returned list is inserted into the final list. This also makes filter expressible through flatMap, returning a singleton list for kept values and an empty list otherwise. section 3.3.3

With Option, flatMap continues only when a value exists. It is needed when the next step itself may fail: map would turn an Option[Double] followed by Double => Option[Double] into Option[Option[Double]], whereas flatMap returns Option[Double]. The variance calculation first finds a mean, then uses that mean to compute a second mean inside the same optional context. section 4.6 section 4.3.1

enum Option[+A]:
  case Some(get: A)
  case None
 
  def map[B](f: A => B): Option[B] = this match
    case None => None
    case Some(a) => Some(f(a))
 
  def getOrElse[B>:A](default: => B): B = this match
    case None => default
    case Some(a) => a
 
  def flatMap[B](f: A => Option[B]): Option[B] =
    map(f).getOrElse(None)
 
  def orElse[B>:A](ob: => Option[B]): Option[B] =
    map(Some(_)).getOrElse(ob)
 
  def filter(f: A => Boolean): Option[A] =
    flatMap(a => if f(a) then Some(a) else None)

This implementation exposes the useful decomposition: map creates a nested Option, and getOrElse(None) removes its outer layer. It also shows filter built from flatMap. section 4.6

The same sequencing need appears when a computation’s next action depends on its previous result. A Rand[A] action consumes an RNG and returns a value plus a new RNG; flatMap passes that new RNG to the Rand[B] selected from the value. This supports retrying nonNegativeLessThan when a generated integer would introduce modulo bias, while keeping state passing explicit and testable. section 6.4.2 section 6.8

Across contexts, flatMap selects a next computation rather than merely transforms a completed value:

  • For Par, it runs an initial parallel computation, then uses its result to determine a second computation; join flattens Par[Par[A]], and flatMap equals map followed by join. section 7.4 section 7.9
  • For Parser, it selects a parser from the first parsed value, advances the input location, tracks consumed characters, and commits failures after consumption. section 9.6.5
  • For streams and pulls, it chooses subsequent pulls or streams from observed values, such as dropping elements, computing sliding means, or discarding invalid temperature lines. section 15.6

When a result determines what to do next, flatMap expresses dynamic structure. Independent Option lookups can use map2, but looking up an employee ID before choosing department and salary lookups requires flatMap. This distinction separates fixed-shape applicative combinations from monadic combinations whose later computations depend on earlier results. section 12.3.1

flatMap is the central operation generalized by Monad alongside unit. Common implementations of map2 for generators, parsers, and options reveal the same pattern: flatMap obtains the first value, then map combines it with the second. Its associative law guarantees that regrouping sequential steps does not change their meaning. section 11.2 section 11.4.1

Dependent employee lookup

An ID lookup can drive later lookups without manual None checks: idsByName.get("Bob").flatMap(id => depts.get(id).map2(salaries.get(id))((dept, salary) => s"Bob in $dept makes $salary per year")). If Bob has no ID, the entire result remains absent; otherwise, the selected ID determines both later lookups. section 12.3.1

How Functional Programming in Scala, Second Edition covers it

How deep it goes
Builds implementations for List, Option, Rand, Par, and Parser, then generalizes flatMap through Monad laws.
Examples
Uses list duplication, optional variance, bounded random numbers, employee lookups, generators, parsers, and temperature-conversion streams.
What you can do after
Build it from scratch for List, Option, and Rand, then apply it to dependent computations across several abstractions.
How it connects
Connects map, map2, filter, join, unit, Monad, Applicative, Option, State, Par, Parser, and Stream.

Also explained in

Compare how 5 books explain flatmap →

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.