liveBook · Manning

Monad

As explained in Functional Programming in Scala, Second Edition

A monad is an abstraction that represents effectful or context-dependent values and provides operations, including flatMap, for sequencing computations while preserving that context.

A monad is a type-constructor interface for computations that carry some context while producing values. The context may represent optionality, generated values, parsing, parallel work, state, or I/O; the interface does not prescribe what happens internally. Instead, it identifies a shared way to build a value with `unit` and sequence a context-aware next step with `flatMap`. section 11.5 section 11.7

The motivating clue is repeated code. `Gen`, `Parser`, and `Option` can each combine two contextual values with the same `flatMap` and `map` structure, even though their meanings differ. A `Monad[F]` factors out that structure: implementing `unit` and `flatMap` for `F` supplies derived operations such as `map` and `map2` once for every instance. section 11.2 section 11.2.1

Listing 11.1 Implementing map2 for Gen, Parser, and Option
extension [A](fa: Gen[A])
  def map2[B, C](fb: Gen[B])(f: (A, B) => C): Gen[C] =   #1
    fa.flatMap(a => fb.map(b => f(a, b)))
 
extension [A](fa: Parser[A])
  def map2[B, C](
    fb: Parser[B])(f: (A, B) => C
  ): Parser[C] =                                         #2
    fa.flatMap(a => fb.map(b => f(a, b)))
 
extension [A](fa: Option[A])
  def map2[B, C](
    fb: Option[B])(f: (A, B) => C
  ): Option[C] =                                         #3
    fa.flatMap(a => fb.map(b => f(a, b)))

Compare the identical `map2` implementations: only the surrounding type changes, revealing the abstraction that Monad captures. section 11.2

`flatMap` matters when the value produced by one computation determines the computation to run next. Unlike `map2`, which combines already chosen computations, it can select later work from an earlier result. This makes monadic computations context-sensitive: an `Option` lookup can use a retrieved ID for later lookups, and a parser can choose a row parser after reading a file header. section 12.3.1 section 12.3.2

A Monad provides a small primitive vocabulary and many reusable combinators:

  • `unit(a)` lifts a value into the context `F[A]`. section 11.2.1
  • `flatMap(f)` sequences a function from `A` to `F[B]`, retaining the context rather than producing a nested result. section 11.2.1 section 12.3
  • `map` transforms a produced value, and `map2` combines two monadic values; both are derived from the primitives. section 11.2.1
  • `sequence`, `traverse`, `replicateM`, `product`, and `filterM` package common patterns for any monad. section 11.3

The interface is constrained by laws, not merely method signatures. Associativity says regrouping chained `flatMap` calls cannot change their result. Left and right identity say that inserting `unit` before or after sequencing changes nothing. These guarantees justify refactoring a sequence of computations without altering its meaning. section 11.4.1 section 11.4.3

Stateful indexing

For `State[S, A]`, `flatMap` passes the updated state from one action to the next. A `zipWithIndex` computation can read an integer state, emit it beside a list element, increment the state, and continue. Once `State[S, _]` has a Monad instance, generic `sequence` and `replicateM` can run repeated state actions in order. section 11.5.2 section 11.7

Monad extends Functor, so every monad supports `map`; it also has capabilities beyond an Applicative. `unit` and `map2` can combine a fixed computation structure, but cannot implement `flatMap` or `join`, which remove a layer of context and enable later structure to depend on earlier results. section 12.3 section 12.9

This sequencing is useful for explicit effects as well. An `IO[A]` value can describe input and output while retaining its result type; `flatMap` builds a larger description, and interpretation later executes it. The same generic monadic combinators can then express repeated reads, loops, and effectful folds. section 13.2.1

How Functional Programming in Scala, Second Edition covers it

How deep it goes
Defines minimal primitives and laws, derives combinators, and works through State, Option, Parser, and IO applications.
Examples
Uses duplicate `map2` implementations, generated orders, dependent map lookups, dynamic CSV parsing, state indexing, and console I/O.
What it assumes
Builds from prior concrete data types and Functor operations, while introducing monadic terminology through repeated implementations.
What you can do after
Build it from scratch by defining `unit` and `flatMap`, deriving combinators, and checking associativity and identity laws.
How it connects
Connects Functor, Applicative, State, Option, Parser, IO, join, and compose through sequencing and derived operations.

Also explained in

Compare how 6 books explain monad →

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.