liveBook · Manning

Map2

As explained in Functional Programming in Scala, Second Edition

map2 is a state-action combinator that combines two actions with a binary function while threading state from the first action into the second.

`map2` combines two values already living in the same context with a binary function. Its result remains in that context: two `Option` values produce an `Option`, two parser results produce a parser result, and two state actions produce a state action. The combinator separates ordinary domain logic—such as a function taking two plain arguments—from the mechanics of failure, parsing, or state. section 4.3.2 section 11.2

For `Option`, `map2(a, b)(f)` yields `Some(f(aa, bb))` only when both inputs contain values; otherwise it yields `None`. This lifts an existing two-argument function without rewriting it to know about optional inputs. An implementation uses `flatMap` on the first option and `map` on the second, avoiding a nested `Option[Option[C]]`. section 4.3.2 section 4.6

Combining parsed insurance inputs

Convert an age and ticket count from strings into `Option[Int]`, then call `map2(optAge, optTickets)(insuranceRateQuote)`. The rate function still accepts two `Int` values; failed conversion is represented by `None` rather than changing that function or manually matching both options. section 4.3.2

For stateful actions, `map2` runs the first action with the initial state, runs the second with the state returned by the first, and combines their produced values. Keeping this state threading inside `map2` removes repeated RNG bookkeeping and makes the sequencing easier to inspect. section 6.8

def map2[A, B, C](ra: Rand[A], rb: Rand[B])(f: (A, B) => C): Rand[C] =
  rng0 =>
    val (a, rng1) = ra(rng0)
    val (b, rng2) = rb(rng1)
    (f(a, b), rng2)

This implementation makes the three state values explicit: the first action consumes `rng0`, the second consumes `rng1`, and the combined action returns `rng2`. section 6.8

The same shape recurs in parsers, generators, `Either`, and `State`. For parsers, `map2` can be defined through `product` followed by `map`; recursive parser uses may require its second parser argument to be nonstrict. For `Either`, it combines two `Right` values while preserving a `Left` failure. section 9.8 section 4.4

Use `map2` when both contextual computations are known before their results are combined:

  • Combine independent `Option` lookups, such as a department and salary lookup for the same name. section 12.3.1
  • Build `sequence` and `traverse` by folding contextual values with list cons as the combining function. section 4.6 section 6.8
  • Combine parser results into lists or structured values, while deferring recursive parsers when necessary. section 9.8

`map2` expresses a fixed computation structure: it combines results of computations selected independently. When the result of the first computation determines which second computation to perform, use `flatMap` or `join` instead. This distinction motivates `map2` as a primitive of `Applicative`, while monads can derive it from `flatMap`. section 12.3.1 section 12.2

How Functional Programming in Scala, Second Edition covers it

How deep it goes
Develops implementations through flatMap, map, for-comprehensions, parser products, and explicit RNG state threading, then abstracts map2 into Applicative.
Examples
Uses insurance-rate parsing, independent Map lookups, random-number actions, list sequencing, parser repetition, and Person validation.
What you can do after
Build it from scratch for Option and state actions, then apply it to combine independent effectful computations.
How it connects
Connects Option, Either, State, Parser, Gen, flatMap, Applicative, Monad, sequence, traverse, product, and apply.

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.