skip to content

In algebraic data types, what distinguishes a payment that is exactly one of card, transfer or credit from a record holding all three?

level: juniorimportance: must knowfreq 72%

answer

  1. two ways to combine types
  2. and-type versus or-type
  3. every component at once, or one alternative
  4. the choice carries a label
  5. real models are choices of records

basics

~20 s

A sum type holds exactly one of its variants, tagged so you can tell which; a product type holds a value of every component at once. The payment is a sum, the record of all three is a product.

solid answer

~40 s

The record is a **product type**: one value carries a card part *and* a transfer part *and* a credit part, all present together, so any field may be read without asking. The payment is a **sum type**: one value is a card *or* a transfer *or* store credit, and it carries a tag naming which variant it was built as. That tag is the half people leave out — a sum is a *labelled* choice, so two variants that happen to carry the same payload shape stay distinct. The practical difference is what the type makes available: from a product, every component; from a sum, the fields of the one variant you are holding, once you have established which one that is.

code

pseudocode · 12 lines
pseudocode
type CardDetails is a record of:
    maskedNumber: text
    expiryMonth: number
    holderName: text

type PaymentMethod is one of:
    Card(details: CardDetails)
    BankTransfer(accountRef: text, routingCode: text)
    StoreCredit(voucherId: text)

// one CardDetails value carries all three fields
// one PaymentMethod value carries one label and that label's payload

go deeper

for a junior

Learn the two words and one example each: a record of fields held together is a product, a closed choice of alternatives is a sum. Say the connecting word out loud — and versus or — and you will not mix them up under pressure.

for a middle

Explain the tag: a sum value carries a label naming its variant, which is why two variants with identical payloads stay distinct. Then show the nesting, because real models are choices whose alternatives each carry their own record of fields.

for a senior

Show the judgment: point at a record in a real model whose fields are meaningful only in certain combinations and say which closed choice it should have been, and what that costs to change once callers depend on the flat shape.

for a principal

Frame it as a standard for other teams: when a domain concept is a closed choice, the model should say so in the type rather than in a comment, and the exception list — transport shapes, sparse storage — should be written down instead of argued case by case.

Every composite type you have ever declared is assembled one of two ways, and *algebraic data types* is simply the name for those two ways plus the arithmetic that falls out of them. ## A product holds every component at once A **product type** glues several component types together so that one value carries a value of each of them simultaneously. Records, structs and tuples are all product types; the names differ, the construction does not. The connecting word is **and**: one set of card details carries a masked number **and** an expiry **and** a holder name. Nothing in the declaration lets a value skip a component — if the type names three fields, a value has three fields. Two consequences follow immediately: - **Every component is readable without asking.** Given a value of the type you may reach for any field; the type has already promised it is there. - **Every combination of component values is a value of the product.** If one component has three possible values and another has two, the product has six. ## A sum holds exactly one variant, and records which A **sum type** declares a fixed set of alternatives, and one value is exactly one of them. The connecting word is **or**: a payment method is a card **or** a bank transfer **or** store credit. The set is closed at the declaration — those three, and a value is not two of them at once and not none of them. The part candidates most often leave out is the **tag**. A sum value does not merely contain one of the payload types; it carries a label naming the variant it was built as. That label is what separates a sum from a loose "some value of one of these types": - Two variants whose payloads have the same shape — say both carry a single amount — stay **distinct**, because the labels differ. - A reader establishes the label first, and the fields that are meaningful are the ones belonging to that variant. - The set of labels is finite and written in one place, so the declaration itself enumerates the possibilities. ## The two side by side | | Product (card details record) | Sum (payment method) | |---|---|---| | Connecting word | and | or | | One value carries | a value of **every** component | a value of **exactly one** variant, plus its label | | Reading a field | always available | available for the variant in hand | | Value count | components multiplied | variants added | | What the declaration says | which fields travel together | which alternatives exist, and that there are no others | ## They nest, and real models use both The two constructions are not rivals; almost every useful type is a **sum of products**. A payment method is a choice of three variants (a sum), and each variant carries the fields that kind of payment needs (a product): the card variant a number and an expiry, the transfer variant an account reference and a routing code, the credit variant a voucher identifier and a balance. The nesting runs the other way too — a record may have a field whose type is itself a choice. That layering is why the vocabulary earns its keep. Once you can say "this is a sum of products", you have described a model's shape in four words and said something checkable: how many alternatives there are, and which fields belong to which. ## Where each shape goes wrong - Using a **product where the domain is a choice** produces a record whose fields are meaningful only in certain combinations, and the rule about which combinations those are lives in documentation or in the reader's head rather than in the type. - Using a **sum where the domain is a conjunction** splits a genuine "all of these at once" record into variants, forcing callers to unwrap something that was never a choice. - Dropping the **tag** leaves "a value of one of these three types" with no way to ask which, which is not a sum type at all. ## What an interviewer is listening for They are checking three things, roughly in this order: 1. That you can say **and-type versus or-type** without reaching for a language's syntax — the construction, not a keyword. 2. That you mention the **tag**, because that is what distinguishes a sum from an untagged overlap of shapes. 3. That you reach for **sum of products** when describing a real model, instead of treating the two as competing styles. Reciting "sum means one of, product means all of" is the floor of the answer. The step past the floor is what each shape lets a reader assume, and therefore what it stops them getting wrong.

  • Two payment variants each carry a single amount and nothing else — what keeps them from being the same type?
    The constructor label. A sum is a *labelled* choice, so a value built as one variant carries that variant's tag and is never read as the other, however identical the payloads look. Drop the labels and you have an untagged overlap, where a reader holding an amount has no way to ask which kind of payment produced it.
  • Can a product contain a sum, and a sum contain products?
    Both, and the combination is the normal shape of a real model. A sum of products is a choice of variants where each variant carries its own record of fields. A product containing a sum is a record with one field whose value is a choice — an order record whose payment field is one of three payment variants.
  • Is a type with a single alternative and no payload still a sum type?
    Yes, and it is the degenerate case worth knowing: a choice of one variant with an empty payload has exactly one value, so it carries no information. That is why a variant with no fields and a variant carrying an empty record are interchangeable — both contribute one value to the total.

A form where you fill in every box is a product; a form where you tick exactly one box at the top and then complete only that section is a sum, and the tick is the tag.

saying these in an interview costs you the question

  • Says a sum type value can hold two variants at once
  • Calls any grouping of fields a sum type
  • Treats the variant label as optional metadata rather than part of the value
  • Assumes a record whose fields may be absent is already a sum
  • Cannot say what a single value of each construction carries