Language Design: The Benefits of Type Unions

Published on 2021-08-26. Last updated on 2026-07-28

Overview

Syntactic Wrapping No Syntactic Wrapping
No Runtime Tags untagged union
(C union, C++ union, Rust union)
union type
(TypeScript union type)
Runtime Tags discriminated union/tagged union
(Rust enum, F# discriminated union)
type union
(Algol united mode, Core union, C# union)


Benefits of Type Unions

Union variants are types, and refer to a “real” class/struct/… definition

This fixes a mistake that some languages like Rust or Haskell made with their enum/data types.12

It also means that variants can be reference types or value types (as they refer to existing type definitions).

Example
class Cat(let name: String, let lives: Int64)
class Dog(let name: String, let years: Int64)
union Pet of Cat, Dog

// accepts both cats and dogs
fun pet(pet: Pet) = ...

// accepts only dogs
fun playFetch(dog: Dog) = ...

No “stutter”, where variant names have to be invented to wrap existing types

This reduces syntactic indirection at definition-sites. (Rust has this issue.)

Example Instead of ...
enum JsonValue {
  JsonObject(Map[String, JsonValue])
  JsonArray (Array[JsonValue]),
  JsonString(String),
  JsonNumber(Float64),
  JsonBool  (Bool),
  JsonNull,
  ...
}
... one would write (with Array, Float64 and String being existing types in the language):
union JsonValue of
  Map[String, JsonValue]
  Array[JsonValue],
  String,
  Float64
  Bool,
  JsonNull,
  ...

value JsonNull

Union values can be passed/created more easily, as no syntactic wrapping is required

This reduces syntactic indirection at use-sites and allows “variants” to be used in more scenarios (compared to more “traditional” enums).

Example No wrapping required when passing arguments (unlike "traditional" enum approaches):
fun someValue(value: JsonValue) = ...
someValue(JsonString("test")) // "traditional" approach
someValue("test")             // proposed union design

Variants can be used in multiple unions.

Example Consider this class definition:
class Name(name: String)
With the proposed union design, Name can be used multiple times: in different unions, and elsewhere.
union PersonIdentifier of
  Name,
  ... // other identifiers like TaxId, Description, PhoneNumber etc.

union DogTag of
  Name,
  ... // other identifiers like RegId, ...

Type unions obviate the need for a separate type alias feature.