liveBook · Manning

Combinator

As explained in Functional Programming in Scala, Second Edition

A combinator is a compositional operation that builds a larger computation from smaller functions or computational structures.

A combinator is a function or value designed for composition: it takes small computations or structures and builds a larger one. Rather than expose representation details, a combinator works through an algebra’s available operations, so functionality can be assembled incrementally and reused. section 7.1.3 section 7.6

The design problem is not to add an operation for every requested feature. Start with a concrete need, then ask what is accidental about it and refine toward a more general operation. A Boolean-based choice between two parallel computations becomes selection by an integer from a list, then selection by a key from a map; the common pattern can be captured by chooser, flatMap, or join. section 7.4

A useful combinator API separates a small primitive core from derived operations:

  • Primitive operations establish the basic algebra and may depend on a chosen representation. section 7.6
  • Derived combinators are defined from existing operations; lazyUnit, for example, is built from unit and fork without knowing Par’s representation. section 7.1.3
  • Laws constrain how operations interact, making the algebra both a design tool and a basis for testing. section 7.6

Combinators recur because different domains often share a small space of functional solutions. Generators can be assembled from a range generator and operations such as unit, boolean, and listOfN. Properties need a result that preserves success or failure information before operations such as && can combine them. Parsers combine primitive recognizers with alternatives, repetition, products, mapping, and flatMap. section 8.1.4 section 8.1.3 section 9.8

Combining parsers

An alternative combinator joins two parsers of the same result type. Repeating that combined parser builds a parser for three tokens, each of which may be "ab" or "cad". The example shows how a grammar-like computation grows from simple recognizers instead of custom parsing code. section 9.1

def choice[A](cond: Par[Boolean])(t: Par[A], f: Par[A]): Par[A]

Compare the narrowly shaped Boolean choice signature with its later generalizations to see how a concrete request exposes a more general compositional operation. section 7.4

How Functional Programming in Scala, Second Edition covers it

How deep it goes
Develops API refinement, primitive-versus-derived distinctions, laws, and implementations while deferring representation in algebraic parser design.
Examples
Uses parallel choice and lazyUnit, generator construction, property conjunction, and repeated alternative parsers.
What you can do after
Build composite operations from a minimal algebra and refine overly specific APIs into reusable combinators.
How it connects
Connects map, flatMap, join, algebraic design, laws, property-based testing, generators, and parser combinators.

Also explained in

Compare how 4 books explain combinator →

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.