jonnifyelixir

F2 · Functional Programming

Algebraic data types

Complex data is built from two operations. A product type holds several values at once — this and that. A sum type is one of several shapes — this or that. Combine them and you can describe any data precisely, then take it apart with pattern matching.

They are called algebraic because counting their values follows ordinary algebra. The number of values a product type can hold is the product of its parts; for a sum type it is the sum of its variants. Tuples and structs are products; tagged tuples like {:ok, value} are sums. Designing data well is choosing the right sums and products.

Sum is a choice, product is a combination

Shape = Circle{r} | Rect{w,h} SUM · exactly one variant is active Circle r = 5 OR Rect w,h |Circle| + |Rect| counts add Point{x, y, z} PRODUCT · every field is present together %Point{ x: 3 y: 7 z: 2 and and |x| × |y| × |z| counts multiply

Shape = Circle{r}
A sum value is one variant now: Circle, holding a radius. Pattern matching reads which.

Product: this and that

A product type carries several fields together. A tuple {a, b} holds an a and a b. The number of values it can take is the number of as times the number of bs — every pairing is possible.

Set how many values each field has.

algebraic data type
a type built from products and sums of other types.
product type
several values held together — a tuple or a struct.
sum type
one of several alternatives — a tagged tuple or variant.
tagged tuple
a tuple whose first element is an atom naming the variant.

{a, b} · every combination is a value

2
3
b values a values


      
2 a-values × 3 b-values = 6 possible values

Idea

Holding fields together multiplies how many distinct values exist.

Elixir

Tuples and defstruct are products; both keep every field at once.

A product type is an and: all fields present together. Its value count is their product.

Sum: this or that

A sum type is one of several variants — never more than one at a time. A result is {:ok, value} or {:error, reason}. The number of values it can take is the values of one variant plus the values of the other.

Set how many values each variant carries.

A | B · a value is exactly one variant

2
3
variant A variant B


      
2 in A + 3 in B = 5 possible values

Idea

Alternatives that cannot coexist add up rather than multiply.

Elixir

The atom tag (:ok, :error) marks which variant it is.

A sum type is an or: one variant at a time. Its value count is their sum.

Taking it apart

Pattern matching is how you consume an algebraic value. A case lists a clause per variant; the value selects the clause whose shape it fits, binding the fields inside. Pick a result and watch the matching clause light up.

Choose the value.

case result do · one clause per variant

case result do {:ok, v} -> "got #{v}" {:error, e} -> "failed: #{e}" :pending -> "still waiting" end


      
{:ok, 42} · matches the first clause · binds v = 42 · "got 42"

F1.08 · pattern matching

Matching reads a value’s shape and binds its parts in one move.

Elixir

A clause per variant makes handling every case visible — and missing ones obvious.

Pattern matching is the eliminator for algebraic data: one clause per shape, fields bound as they match.

Three deep dives

This module continues across three pages: products in full, sums in full, and the pattern matching that takes both apart. Read them in order.

F2.07.1
Product types
Tuples and structs — fields by position or name, immutable update, and why inhabitants multiply.
F2.07.2
Sum types
Tagged tuples and variants — one shape or another, and the {:ok, _} | {:error, _} idiom.
F2.07.3
Pattern matching on data
Destructuring products to bind fields, and dispatching on sum variants with case and function heads.

What this lands

Algebraic data types give you two ways to combine types and a way to take the result apart. A product holds fields together and multiplies the possibilities; a sum offers alternatives and adds them; pattern matching reads which shape a value has and binds its parts. Most data you model is some nesting of these — a struct of fields, one of which is a tagged variant. The three deep dives above cover products, sums, and matching in turn; F2.08 then composes the functions over them into pipelines.

Start with product types, then sum types, then pattern matching on data. Next module: F2.08 — Composition & pipelines.