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
