Closed algebraic data types (ADTs) with keyword dispatch.
defadt defines sum types that are:
case)Runtime representation: plain maps with :adt/type and :adt/variant keys. This keeps ADT values printable, serializable, and compatible with all Clojure map operations.
Usage: ;; Data-carrying variants (defadt EventType "Event types for hivemind communication." [:event/started {:task string?}] [:event/progress {:message string?}])
;; Enum variants (no associated data) (defadt SpawnMode "Agent spawn modes." :spawn/vterm :spawn/headless)
;; Mixed (defadt Shape [:shape/circle {:radius number?}] [:shape/rect {:w number? :h number?}] :shape/point)
Generated API: (event-type :event/started {:task "X"}) ; constructor (event-type? x) ; type predicate (->event-type :event/started) ; keyword coercion (adt-case EventType evt ; exhaustive match :event/started (str (:task evt)) :event/progress (str (:message evt)))
Closed algebraic data types (ADTs) with keyword dispatch.
defadt defines sum types that are:
- Closed at definition site (variant set is fixed at macro-expansion time)
- Dispatched via keyword (works with Clojure `case`)
- Coercible from keywords (:event/started → variant value)
- Serializable as plain EDN maps (round-trip safe)
- Registered in a global type registry
Runtime representation: plain maps with :adt/type and :adt/variant keys.
This keeps ADT values printable, serializable, and compatible with all
Clojure map operations.
Usage:
;; Data-carrying variants
(defadt EventType
"Event types for hivemind communication."
[:event/started {:task string?}]
[:event/progress {:message string?}])
;; Enum variants (no associated data)
(defadt SpawnMode
"Agent spawn modes."
:spawn/vterm
:spawn/headless)
;; Mixed
(defadt Shape
[:shape/circle {:radius number?}]
[:shape/rect {:w number? :h number?}]
:shape/point)
Generated API:
(event-type :event/started {:task "X"}) ; constructor
(event-type? x) ; type predicate
(->event-type :event/started) ; keyword coercion
(adt-case EventType evt ; exhaustive match
:event/started (str (:task evt))
:event/progress (str (:message evt)))(adt-case type-ref expr & clauses)Exhaustive pattern match on an ADT value's variant keyword.
Checks at macro-expansion time that all variants are covered — compile-time
exhaustiveness checking. Uses case under the hood for O(1) dispatch.
type-ref must be a symbol naming a defadt type (e.g. EventType), or a type metadata map. The type's defadt namespace must be required so the type is in the registry at macro-expansion time.
(adt-case EventType evt :event/started (str "task: " (:task evt)) :event/progress (str "msg: " (:message evt)) :event/completed "done")
Compile-time error if:
Exhaustive pattern match on an ADT value's variant keyword. Checks at macro-expansion time that all variants are covered — compile-time exhaustiveness checking. Uses `case` under the hood for O(1) dispatch. type-ref must be a symbol naming a defadt type (e.g. EventType), or a type metadata map. The type's defadt namespace must be required so the type is in the registry at macro-expansion time. (adt-case EventType evt :event/started (str "task: " (:task evt)) :event/progress (str "msg: " (:message evt)) :event/completed "done") Compile-time error if: - Any variant is missing (non-exhaustive) - Any unknown variant is listed (typo detection)
(adt-data x)Extract the data fields from an ADT value (everything except :adt/type and :adt/variant).
Extract the data fields from an ADT value (everything except :adt/type and :adt/variant).
(adt-type x)Extract the type keyword from an ADT value.
Extract the type keyword from an ADT value.
(adt-valid? x)True if x is a valid ADT value with a registered type and known variant.
True if x is a valid ADT value with a registered type and known variant.
(adt-variant x)Extract the variant keyword from an ADT value.
Extract the variant keyword from an ADT value.
(adt? x)True if x is any ADT value (has :adt/type and :adt/variant keys).
True if x is any ADT value (has :adt/type and :adt/variant keys).
(coerce-keyword type-kw variant-kw)Coerce a keyword to an ADT variant value (no data fields). Returns nil if the keyword is not a valid variant of the type.
(coerce-keyword :EventType :event/started) => {:adt/type :EventType, :adt/variant :event/started}
Coerce a keyword to an ADT variant value (no data fields).
Returns nil if the keyword is not a valid variant of the type.
(coerce-keyword :EventType :event/started)
=> {:adt/type :EventType, :adt/variant :event/started}(defadt type-name docstring? & variants)Define a closed algebraic data type (sum type) with keyword dispatch.
Each variant is either:
Generates:
TypeName var with type metadata {:type :TypeName :variants #{...} :schemas {...}}type-name constructor fn (kebab-case of TypeName)type-name? predicate fn->type-name keyword coercion fn (returns nil for invalid keywords)Example: (defadt EventType "Event types for hivemind communication." [:event/started {:task string?}] [:event/progress {:message string?}] :event/completed)
(event-type :event/started {:task "X"}) ;; => {:adt/type :EventType, :adt/variant :event/started, :task "X"}
(event-type? x) ;; => true/false
Define a closed algebraic data type (sum type) with keyword dispatch.
Each variant is either:
- A bare keyword (enum variant, no associated data)
- A vector [keyword schema-map] (data variant with typed fields)
Generates:
- `TypeName` var with type metadata {:type :TypeName :variants #{...} :schemas {...}}
- `type-name` constructor fn (kebab-case of TypeName)
- `type-name?` predicate fn
- `->type-name` keyword coercion fn (returns nil for invalid keywords)
Example:
(defadt EventType
"Event types for hivemind communication."
[:event/started {:task string?}]
[:event/progress {:message string?}]
:event/completed)
(event-type :event/started {:task "X"})
;; => {:adt/type :EventType, :adt/variant :event/started, :task "X"}
(event-type? x) ;; => true/false(deserialize m)Deserialize a plain map to a validated ADT value. Returns the map if valid, nil otherwise.
(deserialize {:adt/type :EventType, :adt/variant :event/started, :task "X"}) => {:adt/type :EventType, :adt/variant :event/started, :task "X"}
Deserialize a plain map to a validated ADT value.
Returns the map if valid, nil otherwise.
(deserialize {:adt/type :EventType, :adt/variant :event/started, :task "X"})
=> {:adt/type :EventType, :adt/variant :event/started, :task "X"}(make-variant type-kw variant-kw)(make-variant type-kw variant-kw data)Construct an ADT variant value. Low-level constructor — prefer the generated constructor from defadt for validation.
(make-variant :EventType :event/started {:task "X"}) => {:adt/type :EventType, :adt/variant :event/started, :task "X"}
Construct an ADT variant value. Low-level constructor — prefer the
generated constructor from defadt for validation.
(make-variant :EventType :event/started {:task "X"})
=> {:adt/type :EventType, :adt/variant :event/started, :task "X"}(register-type! type-kw type-meta)Register an ADT type with its variant set and schemas. Called by defadt macro — not for direct use.
Register an ADT type with its variant set and schemas. Called by defadt macro — not for direct use.
(registered-types)Return the map of all registered ADT types.
Return the map of all registered ADT types.
(serialize x)Serialize an ADT value to a plain map (EDN-safe). This is effectively identity since ADT values ARE plain maps, but ensures the return is a plain hash-map (not a record or subtype).
Serialize an ADT value to a plain map (EDN-safe). This is effectively identity since ADT values ARE plain maps, but ensures the return is a plain hash-map (not a record or subtype).
(type-registered? type-kw)True if the given type keyword is registered.
True if the given type keyword is registered.
(type-variants type-kw)Return the set of variant keywords for a registered type. Returns nil if type is not registered.
Return the set of variant keywords for a registered type. Returns nil if type is not registered.
(validate type-meta x)Validate an ADT value against its type's schema. Returns the value if valid, throws ex-info if invalid. Use for boundary validation (API entry points, deserialization).
type-meta is the var value from defadt (e.g. EventType).
(validate EventType (event-type :event/started {:task "X"})) => {:adt/type :EventType, :adt/variant :event/started, :task "X"}
Validate an ADT value against its type's schema.
Returns the value if valid, throws ex-info if invalid.
Use for boundary validation (API entry points, deserialization).
type-meta is the var value from defadt (e.g. EventType).
(validate EventType (event-type :event/started {:task "X"}))
=> {:adt/type :EventType, :adt/variant :event/started, :task "X"}cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |