A pair of a three-variant payment type and a boolean flag — how many distinct values can it take?
answer
- count the values a type admits
- record multiplies, choice adds
- combinations against accumulation
- absent adds one, not double
- one-value component leaves count unchanged
basics
~20 sSix. A pair is a product, so its value count is the product of its components' counts: three payment variants times two flag values. Multiplication for a record and addition for a choice are the two operations the word algebraic names.
solid answer
~40 sSix: three times two. Counting a type's **inhabitants** — how many distinct values it admits — is what makes the word *algebraic* literal. A product multiplies its components' counts, because every combination of a payment variant with a flag value is a distinct pair. A closed choice adds its variants' counts instead, because a value is exactly one of them: a choice of the same payment type or the same flag has five values, not six. The arithmetic keeps going: a field that may also be absent has one more value than the field's type had, and a type with a single value multiplies by one, so adding it changes nothing.
code
pseudocode · 13 linestype PaymentMethod is one of: Card | BankTransfer | StoreCredit // 3 values
type Flag is one of: Set | Clear // 2 values
type Selection is a record of:
method: PaymentMethod
isDefault: Flag
// 3 * 2 = 6 values: Card/Set, Card/Clear, BankTransfer/Set,
// BankTransfer/Clear, StoreCredit/Set, StoreCredit/Clear
type MethodOrFlag is one of:
UsesMethod(m: PaymentMethod)
UsesFlag(f: Flag)
// 3 + 2 = 5 valuesgo deeper
Get the two operations straight with a tiny example you can recompute on the spot: a record of a three-value component and a two-value component has six values, a choice of the same two has five.
Be able to derive the number rather than recall it, and handle optionality as plus one on the component before multiplying through the record. Knowing the one-value and zero-value edges shows the arithmetic is real to you.
Use the count as a review argument: state how many combinations a proposed shape admits against how many the domain has, so the modelling discussion turns on a number instead of taste.
Watch for the counting argument being used as a hammer: two shapes with equal inhabitant counts are equally expressive, so the decision between them belongs to readability and migration cost, and saying that stops a team relitigating it.
The word *algebraic* in "algebraic data types" is not decoration. Count how many distinct values each construction admits and the two operations that appear are ordinary multiplication and addition. ## Counting inhabitants The **inhabitants** of a type are the distinct values it can take. A boolean flag has two. A closed payment choice of card, bank transfer and store credit — taking the variants as bare labels for the moment — has three. A type with exactly one value, often used as a placeholder payload, has one. A type with no values at all has zero, and no value of it can ever be built. With those counts in hand, the two constructions behave like arithmetic operators. ## The arithmetic | Construction | Operation | Worked example | |---|---|---| | Product (record of two components) | multiply | payment (3) paired with flag (2) = **6** | | Sum (closed choice of two alternatives) | add | payment (3) or flag (2) = **5** | | A component that may be absent | add one | payment (3) or absent = **4** | | A component of a one-value type | multiply by one | payment (3) paired with placeholder (1) = **3** | | A component of a zero-value type | multiply by zero | payment (3) paired with uninhabited (0) = **0** | The pair is six because every payment variant may be combined with every flag value, and each such combination is a separate pair: card-with-flag-set, card-with-flag-clear, transfer-with-flag-set, and so on down to six. The choice is five because a value is a payment **or** a flag, never both, so the possibilities merely accumulate. ## Why the counting is worth doing 1. **It tells you how much a shape lets you say.** A record with five components that may each be absent admits thirty-two presence combinations; a choice of three alternatives admits three. If the domain has three cases, the first shape gives a reader twenty-nine possibilities to rule out by reasoning rather than by construction. 2. **It tells you when two shapes carry the same information.** Any two finite types with the same inhabitant count can be mapped onto each other reversibly. A boolean flag and a two-variant choice carry exactly the same information, so arguing about which one to use is a naming and readability argument, not an expressiveness one. 3. **It explains the degenerate cases.** A one-value payload contributes nothing, which is why a variant declared with no fields and a variant carrying an empty record are interchangeable. A component whose type has no values makes the whole record impossible to build. ## The edges that make it real arithmetic - The **one-value type** behaves as the multiplicative identity: pairing anything with it leaves the count unchanged. - The **zero-value type** behaves as the additive identity in a choice — adding an alternative nobody can construct adds no values — and as zero in a product, where it makes the whole product uninhabited. - **Optionality is addition, not doubling.** A field of a three-value type that may also be absent has four values, because absence adds one alternative. Saying it doubles to six is the common slip, and it is the reason people overestimate how much an optional field costs when the underlying type is large and underestimate it when the type is small. - The arithmetic extends past the two operations here: the number of distinct functions from a type with *n* values to one with *m* values is *m* raised to the *n*, which is why the family is sometimes drawn out to exponentiation as well. ## Using the count in an argument The count turns a modelling opinion into a number. "This record admits sixty-four field combinations and the domain has three cases" is a sentence a reviewer can check, and it is far harder to wave away than "this feels wrong". It also disciplines the other direction: if two candidate shapes have the same inhabitant count, the choice between them is about which one reads better, not which one is safer, and you should say so rather than inventing a correctness argument. ## What an interviewer is checking They want to see that the word *algebraic* means something to you beyond a label — that you can produce the number, say which operation each construction performs, and get optionality right as **plus one** rather than a doubling. Candidates who have only memorised the names usually answer the pair question with five, or answer the choice question with six, and the two mistakes are the same mistake in different directions.
- What is the count if one component of the pair is a type with exactly one value?Unchanged — three times one is three. A one-value component carries no information, so pairing with it adds nothing a reader can learn. That is the counting explanation for why a variant declared with no payload and a variant carrying an empty record are the same thing.
- If one component of a record has no values at all, how many records exist?None. You cannot build the record because you cannot supply that component, and three times zero is zero. It is a useful check when a generated or derived type turns out to be impossible to construct: look for a component whose own type is uninhabited.
- Does making the flag optional as well change the pair's count, and by how much?It rises from six to nine: the flag's two values become three once absence is an alternative, and three times three is nine. Optionality is addition on the component, which then multiplies through the record — which is how a handful of optional fields grows a record's value count so quickly.
saying these in an interview costs you the question
- Adds the components of a record instead of multiplying them
- Multiplies the alternatives of a choice instead of adding them
- Says an optional field doubles the count rather than adding one
- Thinks inhabitant counting only works for tiny types
- Cannot say what a type with exactly one value contributes