Tagged unions
Under discussion, leaning towards Proposal A: a
uniondeclaration whose members are written like struct fields, tagged with@id. More arguments are being gathered before deciding.
The question. A value that is one of several shapes, with a tag saying
which, is everywhere: sub-packets picked by a sub-type byte
(packet dispatch), an
entity action that is attack, interact or interact-at, a result that is a
success or a failure. Is that a feature of enum or of union?
Where things stand:
unionexists only inline and untagged, asunion(string u32). TypeScript guesses which member arrived from its JSON kind, and the Rust generator refuses it (comline-rust#51).- Enums are bare variants that travel by name.
- Both need explicit numeric values on a packet wire: phases need them for enums, packets for tags. core#126 says the two should share one mechanism.
Three proposals answer the question, written to the same shape. Tracked in core#126.
| Shape | In one line | |
|---|---|---|
| Proposal A | union EntityAction { attack: Attack … } |
a union declaration; members written like struct fields, tagged with @id |
| Proposal B | enum EntityAction { Attack(target: u32) … } |
enum variants carry data, as in Rust and Swift |
| Proposal C | both of the above | union for "one of these types", enums that may carry data |
How they compare
A union declaration |
B enum variants with data | C both | |
|---|---|---|---|
Enums stay plain named values (phases, @open, C-style) |
yes | no: rules must forbid data where it can't be | no |
| Constructs for "one of these" | one | one | two that overlap |
| Payload written inline | with the inline-fields addition | yes | yes |
| Generated Rust | an enum |
an enum |
an enum either way |
| Generated TypeScript | a discriminated union, matching the name | a discriminated union under the name "enum" | both |
| Familiar to | Protobuf (oneof), TypeScript, C |
Rust, Swift, Kotlin | |
The inline untagged union(A B) |
needs a decision | unaffected | needs a decision |
All three share:
- Explicit values with @id, the same annotation as
function ids, on union members and enum
variants alike.
- The wire: the tag travels before the member's fields, at the width the
encoding states. In JSON it's an object carrying the tag and the fields; in
msgpack@1 it's an array.
- Version comparison: a changed tag is major, and adding a member is minor
with the same caveat as enum variants. @open applies to unions too,
generating an Unknown case
(schema compatibility).
Open, whichever is chosen
- The inline untagged
union(A B): keep it for JSON interop, where an untagged union is how many existing APIs are shaped (with an encoding profile fact saying which encodings support it), or retire it in favour of the declared tagged form. - Default values: an enum field's default is its first variant today. What is the default of a tagged value whose members carry data?