# Metaprogramming
Deft exposes its own metadata and reflection layer via `@`-decorators,
the `meta` accessor, and the `info/*` namespace. Together they cover
introspection (what does this value know about itself?) and
annotations (what should tooling do with this?). Special forms — the
compiler-reserved names that desugar to internal IR — are also
documented here.
## Decorators: `@`-tags
A function body may carry any number of `@key value` lines. The key
becomes metadata; the value is attached. `@doc` is the most common;
arbitrary tags work too.
def greet {|name^string| @doc "Print a greeting." @since "1.2.0" @category :friendly echo "Hello, $name!" }
`@doc` accepts either a string (joined if multiple) or a structured
map:
def create-user {|name^string email^string| @doc { :description "Create a user record." :params %{ :name "User's full name" :email "Contact email" } :returns %User } %User{ :name $name :email $email } }
### Retrieving metadata
`meta` reads a single key; `info/props` returns the whole metadata
map:
echo [meta $greet "doc"] # => "Print a greeting." echo [meta $greet "since"] # => "1.2.0" echo [info/props $greet] # => %{ :doc "Print a greeting." :since "1.2.0" :category :friendly }
### Other places decorators are used
| Tag | Used by |
|---|---|
| `@doc` | Editor hover, REPL help, `info/props`, MCP tool descriptions |
| `@rpc true` | Runtime RpcRegistry — exposes fn to `rpc` |
| `@command` | CLI subcommand registration |
| `@private` / `@internal` | Documentation tools — marks non-public |
| `@since` / `@deprecated` | Version tracking |
| `@bench` | Marks benchmarks for the runner |
## The `info/*` Namespace
`info/*` exposes the runtime's symbol and module tables.
| Function | Returns |
|---|---|
| `info/modules` | List of loaded module names |
| `info/defs` | Defs in the current namespace |
| `info/vars` | Vars (module locals) in the current namespace |
| `info/types` | Registered types |
| `info/protocols` | Registered protocols |
| `info/props` | Full metadata map for a value |
| `info/sig` | Function signature |
| `info/source` | Source code of a def (when available) |
| `info/resolve` | Resolve a name to its source |
[info/defs] # => @{ greet create-user ... } [info/types] # => @{ User Point ... } [info/protocols] # => @{ Drawable Keyable ... } [info/sig $greet] # => function with 1 param [info/source $greet] # => source text
## Reflection on Values
[type 42] # => "number" [type "hi"] # => "string" [type %User{...}] # => "User" [type @{ 1 2 }] # => "list" [type ^{:a :b}] # => "set" [is? "number" 42] # => true [is? "User" $alice] # => true [satisfies? $p "Drawable"] # => true if $p's type impls Drawable [nil? $x] # => true if nil [empty? $xs] # => true if empty [future? $f] # => true if a Future [keyword? :foo] # => true
## Special Forms
These are compiler-reserved names. They're not functions; they
desugar to internal IR or trigger specific compiler behaviour.
### Variable binding
| Form | Purpose |
|---|---|
| `def name value` | Module-scoped binding |
| `set name value` | Local rebinding |
| `set name^type value` | Typed rebinding (validates at runtime) |
### Collection constructors
| Form | Purpose |
|---|---|
| `@{ ... }` | List literal |
| `%{ ... }` | Map literal |
| `^{ ... }` | Set literal |
| `%Tag{ ... }` | Tagged map literal |
### Control flow
| Form | Purpose |
|---|---|
| `if cond { ... } else { ... }` | Conditional |
| `match expr { pat => result ... }` | Pattern dispatch |
| `for x in $xs { ... }` | Collection iteration |
| `while cond { ... }` | Condition loop |
| `try { ... } catch e { ... }` | Error handling |
| `defer { ... }` | Cleanup on exit |
| `return expr` | Early return |
| `break` / `continue` | Loop control |
| `throw value` | Raise an error |
### Function definitions
| Form | Purpose |
|---|---|
| `def name { \|params\| body }` | Function |
| `def name { \|params\| body1 \|params\| body2 }` | Multi-arity function |
| `defcoro name { ... }` | Coroutine |
| `deftask { body }` | Async task (also a function-position form) |
| `defworker "path.dft" ?n` | Persistent worker / pool (function-position form; see [defworker](defworker)) |
| `defmacro name { \|params\| body }` | Macro |
| `deftype Name %{ ... }` | Record type |
| `defprotocol Name %{ ... }` | Protocol spec set |
| `defimpl Type Protocol { ... }` | Attach behaviour |
| `defmethod Type name { ... }` | Add one method |
| `defsuite "name" { ... }` | Test suite |
| `deftest "name" { ... }` | Test case |
| `defbench "name" { ... }` | Benchmark case |
> The following forms appear in some older docs but **do not exist**
> in the current language: `defgen`, `defwait`, `defgroup`, `defasync`,
> `chan`, `<-`, `select` (channel form), `receive`, `send`. See
> [Concurrency](concurrency) for the supported primitives.
### Module system
| Form | Purpose |
|---|---|
| `import "path" as alias` | Load and alias |
| `from "path" import @{ names }` | Pull in specific names |
| `implements Interface` | Module conformance (checked at load) |
| `in-ns "name"` | Switch namespace |
### Operators (arity-locked)
| Form | Purpose |
|---|---|
| `+ - * /` | Arithmetic |
| `mod` | Modulo |
| `== != < > <= >=` | Comparison |
| `and or not` | Boolean |
| `& \| ^ << >>` | Bitwise |
| `..` | Range (inside patterns and infix) |
### Internal IR forms
The compiler emits a few `__`-prefixed IR nodes that you may
encounter when reading generated code or macro output:
| Form | Purpose |
|---|---|
| `__make-tagged-map` | Construct a tagged map (used by HTML/SQL blocks) |
| `__shell-exec` | Shell capture `[|cmd]` |
| `__postfix-*` | Postfix sugar `$x(call)` |
| `__capture` | Closure literal |
| `__import-file` / `__import` / `__refer` | Module loader hooks |
| `__implements` | `implements` desugaring |
| `__source` | Source-tracking node |
| `__defsuite` / `__deftest` / `__defbench` | Test scaffolding |
You don't need to write these directly; they're documented here so
macro output and error traces are decipherable.
## Using Metadata in Practice
### Editor / tooling
Documentation tools walk `info/defs` and pull `@doc` strings to build
API references. The MCP integration (see [MCP](../05-AI/mcp)) uses
`@doc` to describe tools exposed to language models.
### Custom dispatch
Tag a function with arbitrary metadata, then dispatch on it:
def handler {|req| @route "/users/:id" @method :GET # ... } # Walk all defs, find handlers by their @route metadata def routes [] for name [info/defs] { set fn [info/resolve $name] set route [meta $fn "route"] if $route { $routes << @{ $route $fn } } }
### Runtime versioning
defn deprecated-op {|x|
@deprecated "use new-op instead"
@since "0.3.0"
# ...
}
A lint tool can walk `info/defs` and warn on any call to a function
tagged `@deprecated`.
## Quick Reference
| Form | Purpose |
|---|---|
| `@key value` (inside def body) | Attach metadata |
| `[meta $fn "key"]` | Read one piece of metadata |
| `[info/props $fn]` | Read all metadata |
| `[info/defs]` / `[info/types]` / `[info/protocols]` | Namespace contents |
| `[info/sig $fn]` / `[info/source $fn]` | Function info |
| `[type x]` / `[is? "Type" x]` / `[satisfies? x "Protocol"]` | Type reflection |