liveBook · Manning

Union type

As explained in F# in Action: A Revised Edition of Get Programming with F#

A discriminated union is an F# type whose values are selected from a fixed set of named cases, each potentially carrying different data.

A union type models an OR relationship: a value is one case from a fixed set, rather than several values that all exist together. In F#, discriminated unions fill the domain-modeling gap that records and tuples leave open, while records and tuples model AND relationships. section 8.3 section 5.7

Figure 8.1 Comparing and contrasting AND and OR types in F#

Use this comparison to choose a record or tuple when values coexist, and a union when exactly one alternative can exist. section 8.3

Declare named cases, with each case carrying only the data it needs. Case data can be a primitive, tuple, record, anonymous record, or another union. The case names act as lightweight functions that construct values of the enclosing union type. section 8.3.1

Contact methods

A customer can have a name, age, and contact method, but the contact method is Email with an address, Telephone with country and number, or Post with address details. Creating Email requires only an address; creating Telephone requires only its country and number. All are ContactMethod values, not separate Email or Telephone types. section 8.3.1

Consume a union with pattern matching. Each branch identifies one case and safely binds that case’s data, so telephone details cannot be read from an email value. A match also keeps behavior together: adding a behavior means adding a function over the cases, whereas adding a case requires updating its matches. section 8.3.2

Use unions to make domain states explicit:

  • Nest unions when an alternative has further alternatives, such as an SMS contact holding either a local or international telephone number. section 8.3.4
  • Replace vague flags and numbers with bounded cases such as YearsAsCustomer, OverdraftStatus, and LoanDecision. section 8.3.4
  • Represent validation failures as named error cases when callers must distinguish and reason about exact errors. section 9.2.3
  • Use a single-case union to distinguish otherwise identical primitive fields and add domain richness. section 8.4.3

Prefer explicit branches over a broad wildcard when completeness matters. With bounded cases, the compiler warns about an omitted case—even inside nested matches—whereas a wildcard can silently absorb a newly added case. This protection can remove the need to test merely whether every union case was handled. section 8.3.4 section 14.1.1

Keep unions as data and place operations in functions or modules. Combined with records, tuples, lists, options, and results, they let a model express both coexisting data and controlled alternatives; custom unions are especially useful for tightly defined error domains. section 10.1.2 section 9.2.6

How F# in Action: A Revised Edition of Get Programming with F# covers it

How deep it goes
Explains declaration, construction, pattern matching, nesting, and exhaustiveness, then applies unions to domain states and errors.
Examples
Uses customer contact methods, local versus international SMS numbers, loan decisions, validation errors, and musical instruments.
What it assumes
Builds on tuples, records, and pattern matching while presenting unions as the remaining core F# type category.
What you can do after
Apply unions to encode bounded domain alternatives and use exhaustive pattern matches to handle each case safely.
How it connects
Connects unions with records, tuples, pattern matching, lists, options, results, exceptions, and single-case types.

Also explained in

Compare how 10 books explain union type →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of F# in Action: A Revised Edition of Get Programming with F#. The section links point to the parts of the book it draws on.