(sigil method)
(sigil method) - Open Dispatch (Multimethods)
Provides define-multi / define-method: Clojure-style multimethods that dispatch over record types, struct types, or core values with one primitive. A base type's method is the inherited default (dispatch walks the type's parent chain), default: registers a catch-all, and methods can be added from any module — solving the expression problem for typed data: a package adds a new type plus its methods without touching the code that dispatches.
Basic Usage
(import (sigil record) (sigil method))
(define-record buffer (name))
(define-record editor-buffer include: buffer (text))
(define-multi describe)
(define-method describe (b editor-buffer) (list 'editor (buffer-name b)))
(define-method describe (b buffer) (list 'buffer (buffer-name b)))
(define-method describe (x default:) (list 'value x))
(describe (editor-buffer name: "*e*")) ; => (editor *e*) own method
(describe (buffer name: "*b*")) ; => (buffer *b*) base method
(describe 42) ; => (value 42) defaultA record subtype WITHOUT its own method inherits the nearest ancestor's method — the parent-chain walk covers records and structs alike.
Dispatch Targets
The TYPE position of define-method accepts:
- a record or struct CONSTRUCTOR (the descriptor is read from its procedure metadata — works across modules)
- a record-type or struct-type DESCRIPTOR value
- a core-type symbol:
'dict 'list 'null 'string 'symbol 'keyword 'number 'boolean 'char 'vector 'bytevector 'procedure - the keyword
default:— the catch-all method
Custom Dispatch Functions
By default a multi dispatches on (value-type first-arg). Pass dispatch: for data-driven dispatch:
(define-multi handle-event dispatch: (lambda (e) (dict-ref e kind:)))
(define-method handle-event (e 'click) ...)Dispatch keys are compared with eq?: use symbols, keywords, characters, small integers, or record/struct type descriptors as keys — NOT strings, lists, or freshly computed values (they would never match a registered method). The keyword default: is reserved as the catch-all key even under custom dispatch functions (the same convention as Clojure's :default).
Live Redefinition
Methods can be (re)registered at any time (e.g. at the REPL); each registration invalidates the multi's dispatch caches, so the next call sees the new method immediately.
Performance
Each multi memoizes resolved dispatch (type -> method after the parent-chain walk) and keeps a monomorphic fast path for the last dispatched type, so steady-state dispatch is one value-type + one eq? on hot monomorphic call sites (e.g. a render loop over one buffer type).
Exports
value-typeprocedureThe dispatch type of any value: a struct-type descriptor for struct instances, a record-type descriptor for record instances, or a core-type symbol for everything else.
(value-type (person name: "A")) ; => #<struct-type person>
(value-type #{}) ; => dict
(value-type "hi") ; => stringtype-parentprocedureThe parent of a dispatch type: struct/record descriptors walk their inheritance chain; core-type symbols have no parent.
type-nameprocedureA readable name for a dispatch type (for error messages).
make-multiprocedureCreate a multimethod. Keyword options:
dispatch:— a function of the call's arguments producing the dispatch key (default:(value-type first-arg))default:— a fallback handler when no method matches (an alternative to registering adefault:method)
(define describe (make-multi))
(multi-add-method! describe buffer (lambda (b) ...))multi?procedureTest if a value is a multimethod.
multi-add-method!procedureRegister a method on a multimethod. TYPE follows the define-method conventions (constructor, descriptor, core-type symbol, or default:). Re-registering a type replaces its method and invalidates the multi's dispatch caches.
multi-methodsprocedureThe multi's registered (dispatch-key . handler) alist — a fresh snapshot for introspection and tooling (mutating it does not affect dispatch; register through multi-add-method!).
define-multivariable(No description)
define-methodvariable(No description)