An algebraic data type is built from two operations: sum (a choice — this OR that, a tagged union) and product (a pair — this AND that, a record). Maybe a = None | Some a is a sum; (a, b) is a product. The payoff is pattern matching: because the type enumerates its shapes, the compiler knows every case and can force you to handle all of them — the dreaded “forgot a case” bug becomes a compile error. Sums and products are the whole grammar; every data structure is some algebra of them.
A list of Maybe values, each tagged None(0) or Some(1). The demo matches on the tag and sums only the Some payloads: live demo
“A union is just a struct with a tag you check by hand.” — when the type is the enumeration of shapes, the checking is the compiler's job, not yours, and a missed case is caught before the program runs. The algebra is what makes the match exhaustive. cited
Sum + product is a closed algebra: |A+B| = |A|+|B|, |A×B| = |A|·|B|. The type literally counts its own inhabitants, and the match must cover them all. ML/Hope
On the canonical compiler, a Maybe list [Some 10, None, Some 20, Some 30] matched by tag sums to 60 — the None contributes nothing: