Skip to content

Enums, Unions & Patterns

Jiang separates finite tag sets from payload-bearing sum types:

  • enum defines named members.
  • union defines a safe tagged union where variants may carry payloads.
enum Mode {
read,
write,
}
Mode mode = Mode.read;
Mode other = .write;

Members may specify values:

enum Priority {
low = 1,
medium,
high,
}

Enum discriminants use Int32 by default. Convert an enum value with a target integer constructor such as Int(value) or UInt8(value); enum cases do not expose a .value member.

Use enum [UInt8] or another integer type when the stored representation must be fixed:

enum [UInt8] ByteTag {
block = 1,
item,
}
UInt8 tag = UInt8(ByteTag.block);

Convert an enum value with a target integer constructor:

Int value = Int(Mode.read);
union Outcome<T, E> {
T ok;
E err;
}
Outcome<Int, ParseErr> a = Outcome.ok(42);
Outcome<Int, ParseErr> b = .err(ParseErr.bad);

Same-type variants can be grouped:

union Value {
Int int_value, code;
Double float_value;
UInt8[]& text;
}

Unions can bind an explicit tag enum:

enum Kind {
int_value,
text,
}
union [Kind] TaggedValue {
Int int_value;
UInt8[]& text;
}
if value is .int_value(_ n) {
print(n);
} else {
print(0);
}

Put ! after the name for a mutable inferred binding:

if value is .int_value(_ n!) {
n = n + 1;
}

Use ref T name to borrow a union payload instead of binding it by value:

union Node {
(Int, Int) pair;
() empty;
}
Int read_ref(Int& value) {
value$.get()
}
Int first(Node node) {
if node is .pair(ref Int left, _) {
return read_ref(left);
}
return 0;
}

This matters when the payload is large, non-copyable, or should be inspected without moving it. In patterns, ref! Int value creates a unique mutable Int&! borrow. Write ref Int value! or ref! Int value! when the resulting reference binding itself must be reassignable.

Optional values use .some(...) and .none:

if maybe is .some(payload) {
print(payload);
} else {
print(0);
}
Int code = switch mode {
.read => 1,
.write => 2,
}

Union payloads:

Int result = switch value {
.int_value(_ n) => n,
.code(_ code) => code,
.float_value(_) => 0,
.text(_ bytes) => bytes.length,
}

Borrowed union payloads use the same ref sub-pattern:

Int result = switch node {
.pair(ref Int left, _) => read_ref(left),
.empty => 0,
}

Optional values:

Int value = switch maybe {
.some(payload) => payload,
.none => 0,
}

is and switch branch roots accept:

  • optional patterns: .some(payload) and .none
  • variant patterns: .name(...) or Type.name(...)
  • literal patterns such as null, numbers, chars, and booleans

Bindings and wildcards are sub-patterns of optional or variant payloads. Tuple patterns are not switch roots; use destructuring statements for tuples.