# 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`