Skip to main content

variant

A discriminated union: a named set of constructors, each carrying zero or more payload types. A value of the type is one constructor and its payloads.

variant Angle {
    DMS(int32, int32, int32);
    Radians(float64);
}

Constructors​

Each constructor is a name, an optional payload list, and a semicolon. The names are scoped to the variant and reached through it, as Angle.Radians.

  • A variant declares at least one constructor.
  • Constructor names are unique within the variant. Arity is not part of the name, so Firm; and Firm(string); collide.
  • A constructor may carry no payload. The list may be omitted or written empty, and Firm; and Firm(); declare the same nullary constructor.
  • A payload type may be the variant being declared, so a route is a variant that holds the rest of itself.
  • message may precede variant, marking the type as something components send each other in a protocol.
variant TrackQuality {
    Firm;
    Tentative();
    Dropped(string);
}

variant Route {
    Stop(string);
    Leg(string, Route);
}

message variant SensorReport {
    Bearing(float32);
    Silent;
}

Values​

A constructor with payloads is applied the way a function is called. A nullary constructor is used bare or with empty parens, and both forms give the same value.

  • The application supplies exactly as many arguments as the constructor has payload types.
  • Each argument must be assignable to its payload type.
  • Payloads have no names, so matching is what reads them back out.
variant TrackQuality {
    Firm;
    Dropped(string);
}

const firm: TrackQuality = TrackQuality.Firm;
const also_firm: TrackQuality = TrackQuality.Firm();

firm == also_firm                // => true
TrackQuality.Dropped("lost")     // => TrackQuality.Dropped("lost")

Matching​

A pattern names a constructor and binds its payloads positionally. Each binding is in scope in that arm's body.

  • A pattern binds exactly as many sub-patterns as the constructor has payload types.
  • A bare TrackQuality.Firm pattern binds none, which is correct for a nullary constructor only.
variant Angle {
    DMS(int32, int32, int32);
    Radians(float64);
}

function whole_degrees(a: Angle) -> int32 = match (a) {
    Angle.DMS(deg, min, sec) => deg;
    Angle.Radians(rad) => 0;
};

whole_degrees(Angle.DMS(45, 30, 0))     // => 45
whole_degrees(Angle.Radians(1.57))      // => 0

Errors​

A variant with no constructors​

variant TrackQuality {}   // error: empty-variant, expected

Two constructors with one name​

The payload list is not part of the name, so a second Dropped collides whatever it carries.

variant TrackQuality {
    Dropped(string);
    Dropped(uint32);   // error: duplicate-variant-constructor
}

Applying a constructor with the wrong number of arguments​

A bare reference counts as zero arguments, so it is an argument short of a constructor that carries a payload.

variant TrackQuality {
    Firm;
    Dropped(string);
}

const A: TrackQuality = TrackQuality.Dropped;         // error: call-arg-count
const B: TrackQuality = TrackQuality.Firm("lost");    // error: call-arg-count

A payload argument of the wrong type​

variant TrackQuality {
    Dropped(string);
}

const A: TrackQuality = TrackQuality.Dropped(7);   // error: call-arg-type

A pattern that binds the wrong number of payloads​

variant TrackQuality {
    Firm;
    Dropped(string);
}

function label(q: TrackQuality) -> string = match (q) {
    TrackQuality.Dropped(reason, code) => reason;   // error: incompatible-pattern
    _ => "firm";
};
  • enum — a closed set of values over a primitive, with no payloads.
  • struct — the record type a payload usually holds.