liveBook · Manning

Single-case discriminated union

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

A single-case discriminated union is a discriminated union with exactly one named case, typically used to create a distinct domain-specific type around another value.

A single-case discriminated union has one named case and is useful as a lightweight wrapper around a value. Wrapping strings such as phone numbers and country codes gives them distinct meanings and prevents accidental mixing. section 8.3.5

Typed telephone values

Define separate wrapper types, then use them inside a larger union: `type PhoneNumber = PhoneNumber of string` and `type CountryCode = CountryCode of string`. An international value can then be created as `International (CountryCode "+44", PhoneNumber "208-123-4567")`. Swapping the values produces a type error instead of allowing an invalid telephone number to travel through the system. section 8.3.5

Use single-case unions when:

  • Two fields share a primitive representation but have different domain meanings, such as a phone number and a country code. section 8.3.5
  • A value should pass through a validated boundary, such as `ValidatedEmail`, which can only be constructed after email validation succeeds. section 8.3.5
  • A record or discriminated union needs extra type distinctions between fields that would otherwise use the same primitive types. section 8.4.3
Listing 8.11 Working with single-case discriminated unions
type PhoneNumber = PhoneNumber of string            #1
type CountryCode = CountryCode of string            #1
 
type TelephoneNumberRevised =
    | Local of PhoneNumber                          #2
    | International of CountryCode * PhoneNumber    #2
 
let localNumber = Local (PhoneNumber "123-456")     #3
let internationalNumber =
    let countryCode = CountryCode "+44"             #4
    let phoneNumber = PhoneNumber "208-123-4567"    #4
    International (countryCode, phoneNumber)        #4

The listing shows wrapper definitions, their use inside a telephone-number union, and the construction of local and international values. section 8.3.5

The wrapper can be removed with a pattern in a `let` binding or function argument, keeping the representation lightweight while preserving the domain distinction. Nested wrappers can also represent validated states. section 8.3.5

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

How deep it goes
Defines wrappers, constructs and unwraps values, demonstrates compiler errors, and applies nested wrappers to validation.
Examples
Uses phone numbers, country codes, international telephone values, and validated email addresses as concrete wrapped domains.
What you can do after
Build it from scratch to create distinct domain values, catch swapped arguments through types, and expose validated states.
How it connects
Connects single-case unions with discriminated unions, pattern matching, records, tuples, validation, and domain modeling.

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.