# Types and Protocols Deft is dynamically typed with optional type hints (see [Functions](functions)). For organising data and behaviour at scale, the language provides `deftype` (declare a record/map shape), `defprotocol` (declare a method spec set), `defimpl` (attach behaviour to a type), and `defmethod` (add a single method to an existing impl). ## Quick Start
 # Define a record with typed fields.
deftype Point %{
    :x %{:type :number}
    :y %{:type :number}
}

# Construct by tagged-map literal.
set p %Point{ :x 3 :y 4 }

# Attach behaviour via a protocol.
defprotocol Drawable %{ draw {ctx bounds} }

defimpl Point Drawable {
    draw {|ctx bounds|
        @doc "Draw an asterisk at the point's coordinates."
        [draw/cell $ctx $self~x $self~y "*"]
    }
}

# Or define a standalone method via defmethod.
defmethod Point magnitude {|| [math/sqrt (($self~x * $self~x) + ($self~y * $self~y))] }

echo [$p magnitude]                  # => 5 
## `deftype` — Record Definitions `deftype` declares a tagged map with field schemas. Each field is a map of options describing its type and metadata.
 deftype User %{
    :name     %{:type :string  :required true}
    :email    %{:type :string}
    :age      %{:type :number  :default 0}
    :roles    %{:type :list    :default @{}}
    :active   %{:type :boolean :default true}
} 
### Field options | Option | Purpose | |---|---| | `:type` | Type hint name (`:string`, `:number`, `:list`, ...) or another `deftype` name | | `:required` | If `true`, construction fails when the field is missing | | `:default` | Used when the field is omitted at construction | | `:description` | Human-readable description surfaced by tooling | > Use keyword keys (`:type`, not `"type"`) in schema maps. Older docs > used string keys (`%{ type "string" }`); that form is deprecated. ### Construction Tagged-map literals produce typed values:
 set alice %User{
    :name "Alice"
    :email "alice@example.com"
    :roles ^{:admin :user}
} 
Field access uses `~`:
 echo $alice~name             # => Alice
echo $alice~roles            # => ^{:admin :user} 
A `deftype` is just a tagged map. You can pattern-match on the tag, use `is?`, or `satisfies?`:
 [is? "User" $alice]                          # => true
[type $alice]                                # => User 
### Kwarg construction (`[Type --field value ...]`) Tagged-map literals (`%User{ :name "Alice" }`) are the canonical construction form, but for types with many fields, defaults, or required constraints, the **kwarg constructor** reads better and validates more strictly. Prefix it with the type name and pass `--field value` pairs:
 deftype User %{
    :first  %{:type :string  :required true}
    :last   %{:type :string  :required true}
    :dept   %{:type :string  :default "accounts"}
    :active %{:type :boolean :default true}
}

set u [User --first "Alice" --last "Smith"]
echo $u~first              # => Alice
echo $u~dept               # => accounts   (default applied)
echo $u~active             # => true       (default applied)

set v [User --first "Bob" --last "Lee" --dept "eng"]
echo $v~dept               # => eng        (overridden) 
Any **single arg starting with `--`** opts the whole call into kwarg mode. In kwarg mode: | Rule | Behaviour | |---|---| | Field names | Each `--flag` must match a declared field (or a field inherited via `<<`). Unknown flags are a hard error. | | `:required` | Enforced — missing required fields raise at construction. | | `:type` | Checked — mismatched values raise at construction. | | `:default` | Applied for any field the call omits. | | Boolean shortcut | A bare `--flag` with no following value means `true` (only valid on `:boolean` fields). | | Positional args | Not allowed. Once you opt in with `--`, every arg must be a flag. |
 [User --first "a" --last "b" --bogus "x"]   # ✗ unknown field '--bogus'
[User --first "only"]                       # ✗ missing required ':last'
[User "posfirst" --last "poslast"]          # ✗ positional args rejected in kwarg mode
[Toggle --admin]                            # ✓ boolean shortcut → admin=true
[Toggle --admin false --disabled true]      # ✓ explicit boolean values 
The positional constructor (`[Point 10 20]`) is unchanged and remains the right choice when field order is obvious and stable — typically for small, positional-natural records like `Point` or `Size`. Reach for the kwarg form when there are ≥3–4 fields, several have defaults, or readability of the call site matters. ### Relation to flag-collection parameters The kwarg constructor and the function-level flag-collection parameter (see [Functions](functions), › Named Optional Arguments) share the same flag-grammar and the same schema. A trailing `opts^Opts` parameter desugars into constructing `%Opts{...}` from the trailing `--flag value` args, so anything you can express with the type's kwarg constructor works the same way at the call site of a function:
 deftype DeployOpts %{
    :replicas %{:type :number  :default 1}
    :dry-run  %{:type :boolean :default false}
}

def deploy {|app^string opts^DeployOpts|
    echo "deploying $app with $opts~replicas replicas"
}

deploy "billing" --replicas 5 --dry-run
# opts is built as if by: %DeployOpts{ :replicas 5 :dry-run true } 
### Extending existing types `<<` lets one type inherit another's fields (mirrors the append operator — it updates the type on the left with the parent's fields):
 deftype Animal %{
    :name %{:type :string}
}

deftype Dog << Animal %{
    :breed %{:type :string}
}

set rex %Dog{ :name "Rex" :breed "Malinois" }
echo $rex~name               # => Rex 
## `defprotocol` — Method Spec Sets A protocol is a named set of method specs (name + arity). `defimpl` methods are validated against those specs at load time.
 defprotocol Drawable %{
    draw {ctx bounds}
}

defprotocol Measurable %{
    measure {}
    natural-height {}
}

defprotocol Focusable %{
    accepts-focus? {}
    on-focus {}
    on-blur {}
} 
The method spec block uses the same `%{...}` map syntax. Each entry is a method name with the parameter names it expects. Type hints on params are allowed:
 defprotocol Gridable %{
    row-count {}
    col-count {}
    cell {row col value opts^CellOpts}
} 
Both `%{...}` and `^{...}` work for the spec block. ### Spec blocks are load-bearing The declared method specs (name + arity) are registered in the type registry, not just the protocol name. `defimpl` methods are validated against those specs — a method that isn't part of the protocol (almost always a typo, e.g. `onkey` vs `on-key`) logs a warning at load time rather than silently creating an unreachable method.
 defprotocol Keyable %{ on-key {event bounds} }

defimpl MyWidget Keyable {
    on-key {|event bounds| ... }     # ✓ matches spec
    onkey {|event bounds| ... }      # ⚠ warned — not in spec
} 
Dispatch itself remains name-based (`resolveMethod(type, "on-key")`); the specs are for validation and `satisfies?`, not runtime routing. ## `defimpl` — Attaching Behaviour `defimpl { ... }` defines all of a protocol's methods on a type in a single block:
 deftype Point %{ :x %{} :y %{} }

defimpl Point Drawable {
    draw {|ctx bounds|
        [draw/cell $ctx $self~x $self~y "*"]
    }
}

defimpl Point Measurable {
    measure {|| %{ :w 1 :h 1 } }
    natural-height {|| 1 }
} 
`$self` inside an impl body refers to the receiver. Methods are dispatched based on the receiver's type tag. ### Dispatch Calls dispatch by name + receiver type. Use bracket form to call:
 def p %Point{ :x 3 :y 4 }
[draw $p $ctx $bounds]                  # routes to Point's draw method
[$p measure]                            # also works (postfix-style) 
### Multi-protocol impls You can also implement multiple protocols in one block when the methods don't clash:
 defimpl MyWidget [Drawable Keyable] {
    draw {|ctx bounds| ... }
    on-key {|event bounds| ... }
} 
> Prefer separate `defimpl` blocks per protocol — it's clearer at the > cost of one extra line each. ### Built-in protocols Deft ships a number of built-in protocols. The widget-level ones (`Drawable`, `Measurable`, `Hittable`, `Keyable`, `Container`, `Textable`, `Editable`, `Selectable`, `Focusable`, `Scrollable`) are declared in `packages/ui/src/protocols.dft` and used pervasively by TUI apps. See [TUI: Widgets and Protocols](../06-TUI/widgets-and-protocols). The four panel-level protocols (`Panel`, `Focusable`, `Scrollable`, `AppLifecycle`) are Zig-registered at TUI startup and carry specs the same way: | Protocol | Methods | |---|---| | `Panel` | `draw {ctx}`, `on-event {event}`, `layout {bounds}`, `children {}` | | `Focusable` | `on-focus {}`, `on-blur {}`, `accepts-focus? {}` | | `Scrollable` | `on-scroll {delta}`, `scroll-offset {}` | | `AppLifecycle` | `on-init {}`, `on-suspend {}`, `on-resume {}`, `on-deinit {}` | Plus any user-defined protocols, all carrying specs the same way. ## `defmethod` — Single-Method Definition `defmethod` adds one method to an existing (or future) impl. Useful when one method is large enough to want its own file or scope.
 defmethod Point magnitude {|| [math/sqrt (($self~x * $self~x) + ($self~y * $self~y))] }
defmethod Point describe {|| "Point($self~x, $self~y)" } 
Parameters live inside `|...|`. `||` for zero params. ## `satisfies?` — Protocol Membership `satisfies?` returns whether a value's type implements a protocol:
 def p %Point{ :x 3 :y 4 }
[satisfies? $p "Drawable"]              # => true
[satisfies? $p "Measurable"]            # => true (if defined)
[satisfies? $p "Container"]             # => false 
## Tagged Maps A `deftype` is essentially a tagged map — a map with a leading tag. You can construct tagged maps directly without a `deftype` declaration:
 set ok-result %Ok{ :value 42 }
set err-result %Err{ :message "broken" }

[is? "Ok" $ok-result]                   # => true
[type $ok-result]                       # => Ok

match $result {
    %Ok{ :value $v }   => $v
    %Err{ :message $m} => [throw $m]
} 
### Tag helpers
 [tag $somemap :Foo]                     # add or change a tag
[untag $foo-map]                        # strip the tag 
`Ok` and `Err` are the canonical tagged maps used by the Result type — see [Error Handling](error-handling). ## Type Introspection For runtime type checks without hints:
 [type 42]                # => "number"
[type "hi"]              # => "string"
[type @{ 1 2 }]          # => "list"
[type %Point{...}]       # => "Point"

[is? "number" 42]        # => true — type name as a STRING
[is? "Point" $p]         # => true
[nil? $x]                # => true if $x is nil
[empty? $xs]             # => true if $xs is empty 
> `is?` takes the type name as a **string** — `is? "number" x`, not > `is? :number x`. The keyword form is no longer supported. ## Comparison: Type Hints vs `deftype` | Aspect | Type Hints | `deftype` | |---|---|---| | Purpose | Annotate params / vars | Define data structures | | Scope | Single def | Module-wide | | Validation | On function call | On construction | | Schema | Single type name | Per-field map of options | | Reuse | Inline | Reusable across functions | They're complementary — `deftype` defines the structure, type hints reference it at function boundaries. ## See Also - [Functions](functions) — parameter syntax and `^` hints - [Pattern Matching](pattern-matching) — destructuring `%Type{...}` and `%{...}` - [Metaprogramming](metaprogramming) — `info/types`, `info/protocols`, reflection - [TUI Widgets and Protocols](../06-TUI/widgets-and-protocols) — built-in UI protocols