# defunion Define a union (sum) type over existing `deftype`s. A value of the union is always one of its variants; there is no union constructor of its own. ## Syntax
defunion Name << VariantA | VariantB | ...
## Examples
deftype Circle %{ :r %{:type :number :required true} }
deftype Square %{ :side %{:type :number :required true} }

defunion Shape << Circle | Square

[is? "Shape" %Circle{ :r 1 }]        # => true
[is? "Shape" %Square{ :side 2 }]     # => true
[type %Circle{ :r 1 }]               # => Circle (the variant, not the union)

match %Square{ :side 2 } {
    ^Shape $s => "a shape"
    _         => "not a shape"
}

# Union-typed fields accept any variant
deftype Drawing %{ :shape %{:type :Shape :required true} }
set d [Drawing --shape %Circle{ :r 1 }]
Schema validation dispatches to the variant's own schema:
[schema/validate :Drawing %{:shape %{:r 1}}]
# => %Err{ :path @{:shape} :message "expected Shape variant, got map" }

[schema/validate :Drawing %{:shape %Circle{ :r "one" }}]
# => %Err{ :path @{:shape :r} :message "expected number, got string" }
## Semantics - Variants must already be defined, and each type may belong to only one union. Unions cannot be extended with `<<`, and a union cannot be a variant of another union. Violations raise when `defunion` runs. - `is?`, `^Union` match patterns, parameter/field type hints, and `schema/validate` accept any variant; `%Variant{}` destructuring stays exact-tag. - `[info/props "Shape"]` exposes `:variants` as a vector of names. - Re-running the same `defunion` (a reload) is a no-op. ## Returns `defunion` is a definition form; it produces no value. Errors (unknown variant, membership conflict, name collision, extending a union) are raised. ## See Also `deftype`, `is?`, `schema/validate`, `schema/from-value`, `info/props`