Unions

A.k.a. Discriminated Unions (DU)

Key points

  • Exact term: Discriminated Union (DU)

  • Sum type: represents an OR, a choice between several Cases

    • Same principle as for an enum, but on steroids πŸ’ͺ

  • Each case must have a Tag (a.k.a Label, Discriminator) -- in PascalCase ❗

  • Each case may contain data

    • As Tuple: its elements can be named -- in camelCase πŸ™

type Ticket =
    | Adult                  // no data -> ≃ singleton stateless
    | Senior of int          // holds an 'int' (w/o more precision)
    | Child of age: int      // holds an 'int' named 'age'
    | Family of Ticket list  // holds a list of tickets
                             // recursive type by default (no 'rec' keyword)

Cases naming

Cases can be used without qualification: Int32 vs IntOrBool.Int32

Qualification can be forced with RequireQualifiedAccess attribute: β€’ Cases using common terms (e.g. None) β†’ to avoid name collision β€’ Cases names are designed to read better/more explicitly with qualification

Cases must be named in PascalCase ❗ β€’ Since F# 7.0, camelCase is allowed for RequireQualifiedAccess unions πŸ’‘

Field labels

Helpful for:

  • Adding meaning to a primitive type β†’ See Ticket previous example: Senior of int vs Child of age: int

  • Distinguish between two fields of the same type β†’ See example below:

Instantiation

Case ≃ constructor β†’ Function called with any case data

Name conflict

When 2 unions have tags with the same name β†’ Qualify the tag with the union name

Unions: get the data out

  • Only via pattern matching.

  • Matching a union type is exhaustive.

Single-case unions

Unions with a single case encapsulating a type (usually primitive)

  • Benefits πŸ‘

    • Ensures type safety unlike simple type alias β†’ Impossible to pass a CustomerId to a function waiting for an OrderId

    • Prevents Primitive Obsession at a minimal cost

  • Trap ⚠️

    • OrderId orderId looks like C# parameter definition

Enum style unions

All cases are empty = devoid of data β†’ β‰  .NET enum based on numeric values πŸ“

Instantiation and pattern matching are done just with the Case. β†’ The Case is no longer a function but a singleton value.

πŸ”— β€œEnum” style unions | F# for fun and profit

Unions .Is* properties

The compiler generates .Is{Case} properties for each case in a union

  • Before Fβ™― 9: not accessible + we cannot add them manually πŸ˜’

  • Since Fβ™― 9: accessible πŸ‘

Last updated

Was this helpful?