logos.multimethod reference
Copy Markdown
Every public, documented Var in logos.multimethod, pulled live from its own docstring, each with a real, freshly-evaluated example and its own source. For prose/narrative explanation and worked examples, see the language reference; for everything else generated (the overview, special forms, primitives, and every other stdlib namespace), see the other pages in this "Stdlib Reference" section.
defmethod
macro -- (defmethod name dispatch-val params & body)
Registers an implementation of name (already defmulti'd) for when its dispatch-fn produces dispatch-val -- :default is a reserved dispatch-val matched when nothing else does.
Example:
(defmulti mm-area (fn [s] (:kind s)))
(defmethod mm-area :square [s] (* (:side s) (:side s)))
(mm-area {:kind :square :side 4})
;;=> 16Source:
(defmacro defmethod
[name dispatch-val params & body]
`(register-method! (quote ~name) ~dispatch-val (fn ~params ~@body)))defmulti
macro -- (defmulti name dispatch-fn)
Defines a multimethod: (name args...) computes (dispatch-fn args...) and calls whichever defmethod was registered for that value (:default, if registered and nothing else matches; otherwise throws :no-method-for-dispatch-value). defmethod calls registered after defmulti take effect immediately, no redefinition needed.
Example:
(defmulti mm-double (fn [x] :default))
(defmethod mm-double :default [x] (* 2 x))
(mm-double 21)
;;=> 42Source:
(defmacro defmulti
[name dispatch-fn]
`(do
(register-multimethod! (quote ~name) ~dispatch-fn)
(defn ~name [& args#] (dispatch-multimethod (quote ~name) args#))))defprotocol
macro -- (defprotocol proto-name & method-specs)
(defprotocol Shape (area [this]) (perimeter [this])) declares each method as an ordinary multimethod (defmulti) dispatching on (type-of this) -- extend-type (below) supplies implementations. Calling a method on a type nobody extend-type'd throws :no-method-for-dispatch-value.
Example:
(defprotocol Sized (size-of [this]))
(extend-type :vector Sized (size-of [this] (count this)))
(size-of [1 2 3])
;;=> 3Source:
(defmacro defprotocol
[proto-name & method-specs]
(cons 'do (defprotocol-multis method-specs)))defprotocol-multis
fn -- (defprotocol-multis specs)
Builds defprotocol's expansion -- one (defmulti method-name protocol-dispatch) per method spec.
Example:
(defprotocol-multis '((size-of [this])))
;;=> ((defmulti size-of protocol-dispatch))Source:
(defn defprotocol-multis
[specs]
(cond
(= specs ()) ()
true
(cons (list 'defmulti (first (first specs)) 'protocol-dispatch)
(defprotocol-multis (rest specs)))))dispatch-multimethod
fn -- (dispatch-multimethod name args)
Looks up name's registered dispatch-fn, calls it with args to get a dispatch value, then calls whichever method matches that value (or :default) with args -- the actual dispatch defmulti's expanded name function calls on every invocation.
Example:
(register-multimethod! 'dm-test (fn [x] :default))
(register-method! 'dm-test :default (fn [x] (inc x)))
(dispatch-multimethod 'dm-test (list 41))
;;=> 42Source:
(defn dispatch-multimethod
[name args]
(let [entry (get multimethod-registry name)
methods (get entry :methods)
dispatch-val (apply (get entry :dispatch-fn) args)
method (get methods dispatch-val (get methods :default))]
(if (nil? method)
(throw :no-method-for-dispatch-value (list :multimethod name :dispatch-value dispatch-val))
(apply method args))))extend-type
macro -- (extend-type type-val proto-name & method-specs)
(extend-type Point Shape (area [this] ...) (perimeter [this] ...)) implements proto-name's methods for type-val -- a defrecord's own type var (e.g. Point, holding its namespace-qualified tag keyword) or a literal built-in type-of tag (e.g. :vector) work identically, since both are just values a protocol method's underlying defmethod dispatches on. Each (method [params] body...) desugars to (defmethod method type-val [params] body...).
Example:
(defprotocol Nameable (name-of [this]))
(extend-type :keyword Nameable (name-of [this] this))
(name-of :abc)
;;=> :abcSource:
(defmacro extend-type
[type-val proto-name & method-specs]
(cons 'do (extend-type-methods type-val method-specs)))extend-type-methods
fn -- (extend-type-methods type-val specs)
Builds extend-type's expansion -- one (defmethod method-name type-val [params] body...) per method spec.
Example:
(extend-type-methods :vector '((size-of [this] (count this))))
;;=> ((defmethod size-of :vector [this] (count this)))Source:
(defn extend-type-methods
[type-val specs]
(cond
(= specs ()) ()
true
(cons
(cons 'defmethod (cons (first (first specs)) (cons type-val (rest (first specs)))))
(extend-type-methods type-val (rest specs)))))multimethod-registry
var
A single, shared map of name -> {:dispatch-fn f :methods {dispatch-val -> method-fn}}, mutated by register-multimethod!/register-method!; see this file's header comment for why it's a plain re-def'd var, not an atom.
Example:
multimethod-registry
;;=> {}Source:
(def multimethod-registry
{})protocol-dispatch
fn -- (protocol-dispatch this & _)
(type-of this), ignoring every argument after the first -- the fixed dispatch-fn every defprotocol method's defmulti uses.
Example:
(protocol-dispatch 5)
;;=> :integerSource:
(defn protocol-dispatch
[this & _]
(type-of this))register-method!
fn -- (register-method! name dispatch-val method-fn)
Registers method-fn as name's implementation for dispatch-val -- the actual mutation defmethod's expansion calls.
Example:
(register-multimethod! 'rm-test (fn [x] :default))
(register-method! 'rm-test :default (fn [x] (* x 2)))
(dispatch-multimethod 'rm-test (list 21))
;;=> 42Source:
(defn register-method!
[name dispatch-val method-fn]
(intern-var! 'logos.multimethod/multimethod-registry
(let [entry (get multimethod-registry name)]
(assoc multimethod-registry name
(assoc entry :methods (assoc (get entry :methods) dispatch-val method-fn))))))register-multimethod!
fn -- (register-multimethod! name dispatch-fn)
Registers name as a multimethod dispatching via dispatch-fn, starting with no methods -- the actual mutation defmulti's expansion calls.
Example:
(register-multimethod! 'reg-test inc)
(contains? multimethod-registry 'reg-test)
;;=> trueSource:
(defn register-multimethod!
[name dispatch-fn]
(intern-var! 'logos.multimethod/multimethod-registry
;; `{:dispatch-fn dispatch-fn :methods {}}` (a literal) would NOT
;; evaluate `dispatch-fn` -- Logos map literals are self-evaluating
;; with UNEVALUATED elements, so that would store the literal symbol
;; `dispatch-fn`, not the function value bound to it (a real bug,
;; caught here by an actual `mix run` smoke test). `assoc` from `{}`
;; instead.
(assoc multimethod-registry name (assoc {} :dispatch-fn dispatch-fn :methods {}))))