Liking cljdoc? Tell your friends :D
Clojure only.

methodical.macros

Methodical versions of vanilla Clojure defmulti and defmethod macros.

Methodical versions of vanilla Clojure [[defmulti]] and [[defmethod]] macros.
raw docstring

defmethodcljmacro

(defmethod multifn-symb dispatch-val docstring? & fn-tail)
(defmethod multifn-symb
           aux-qualifier
           dispatch-val
           unique-key?
           docstring?
           &
           fn-tail)

Define a new multimethod method implementation. Syntax is the same as for vanilla Clojure defmethod, but you may also define auxiliary methods by passing an optional auxiliary method qualifier before the dispatch value:

;; define a new primary method
(defmethod some-multifn Bird
[_]
...)

;; define a new *auxiliary* method
(defmethod some-multifn :before Toucan
[_]
...)
Define a new multimethod method implementation. Syntax is the same as for vanilla Clojure [[defmethod]], but you may
also define auxiliary methods by passing an optional auxiliary method qualifier before the dispatch value:

```clj
;; define a new primary method
(defmethod some-multifn Bird
[_]
...)

;; define a new *auxiliary* method
(defmethod some-multifn :before Toucan
[_]
...)
```
sourceraw docstring

defmulticljmacro

(defmulti name-symb
          docstring?
          attr-map?
          &
          {:keys [dispatcher combo method-table cache]})
(defmulti name-symb
          docstring?
          attr-map?
          dispatch-fn?
          &
          {:keys [hierarchy default-value prefers combo method-table cache]})

Creates a new Methodical multimethod named by a Var. Usage of this macro mimics usage of [[clojure.core/defmulti]], and it can be used as a drop-in replacement; it does, however, support a larger set of options. Note the dispatch-fn is optional (if omitted, then identity will be used). In addition to the usual :default and :hierarchy options, you many specify:

  • :combo - The method combination to use for this multimethods. Method combinations define how multiple applicable methods are combined; which auxiliary methods, e.g. :before or :after methods, are supported; and whether other advanced facilities, such as next-method, are available. There are over a dozen method combinations that ship as part of Methodical; many are inspired by their equivalents in the Common Lisp Object System. The default method combination is the thread-last method combination.

  • :dispatcher - The dispatcher handles dispatch values when invoking a multimethod, and whether one dispatch value (and thus, whether its corresponding method) is considered to be more-specific or otherwise preferred over another dispatch value. The default dispatcher largely mimics the behavior of the Clojure dispatcher, using a single hierarchy augmented by a prefers table to control dispatch, with one big improvement: when dispatching on multiple values, it supports default methods that specialize on some args and use the default for others. (e.g. [String :default])

    Note that the :hierarchy, :default-value and the positional dispatch-fn are provided as conveniences for creating a default dispatcher; if you pass a :dispatcher arg instead, those arguments are not required and will be ignored.

  • :cache - controls caching behavior for effective methods. The default simple cache mimics the behavior of vanilla Clojure multimethods.

  • :method-table - maintains tables of dispatch value -> primary method and auxiliary method qualifier -> dispatch value -> methods. The default implementation is a pair of simple maps.

The above options comprise the main constituent parts of a Methodical multimethod, and the majority of those parts have several alternative implementations available in methodical.impl. Defining additional implementations is straightforward as well: see methodical.interface for more details.

Other improvements over [[clojure.core/defmulti]]:

  • Evaluating the form a second time (e.g., when reloading a namespace) will not redefine the multimethod, unless you have modified its form -- unlike vanilla Clojure multimethods, which need to be unmapped from the namespace to make such minor tweaks as changing the dispatch function.

Attribute map options:

defmulti supports a few additional options in its attributes map that will be used to validate defmethod forms during macroexpansion time. These are meant to help the users of your multimethods use them correctly by catching mistakes right away rather than waiting for them to pull their hair out later wondering why a method they added isn't getting called.

  • :dispatch-value-spec -- a spec for the defmethod dispatch value:

    (m/defmulti mf
      {:arglists '([x y]), :dispatch-value-spec (s/cat :x keyword?, :y int?)}
      (fn [x y] [x y]))
    
    (m/defmethod mf [:x 1]
      [x y]
      {:x x, :y y})
    ;; => ok
    
    (m/defmethod mf [:x]
      [x y]
      {:x x, :y y})
    ;; failed: Insufficient input in: [0] at: [:args-for-method-type :primary :dispatch-value :y] [:x]
    

    Note that this spec is applied to the unevaluated arguments at macroexpansion time, not the actual evaluated values. Note also that if you want to allow a :default method your spec will have to support it.

  • :defmethod-arities -- a set of allowed/required arities that defmethod forms are allowed to have. defmethod forms must have arities that match all of the specified :defmethod-arities, and all of its arities must be allowed by :defmethod-arities:

    (m/defmulti ^:private mf
      {:arglists '([x]), :defmethod-arities #{1}}
      keyword)
    
    (m/defmethod mf :x [x] x)
    ;; => ok
    
    (m/defmethod mf :x ([x] x) ([x y] x y))
    ;; => error: {:arities {:disallowed #{2}}}
    
    (m/defmethod mf :x [x y] x y)
    ;; => error: {:arities {:required #{1}}}
    
    
    (m/defmethod mf :x [x y] x)
    ;; => error: {:arities {:required #{1 [:>= 3]}, :disallowed #{2}}}
    

    :defmethod-arities must be a set of either integers or [:> n] forms to represent arities with & rest arguments, e.g. [:>= 3] to mean an arity of three or-more arguments:

    ;; methods must both a 1-arity and a 3+-arity
    (m/defmulti ^:private mf
      {:arglists '([x] [x y z & more]), :defmethod-arities #{1 [:>= 3]}}
      keyword)
    
    (m/defmethod mf :x ([x] x) ([x y z & more] x))
    ;; => ok
    

    When rest-argument arities are used, Methodical is smart enough to allow them when appropriate even if they do not specifically match an arity specified in :defmethod-arities:

    (m/defmulti ^:private mf
      {:arglists '([x y z & more]), :defmethod-arities #{[:>= 3]}}
      keyword)
    
    (m/defmethod mf :x
      ([a b c] x)
      ([a b c d] x)
      ([a b c d & more] x))
    ;; => ok, because everything required by [:>= 3] is covered, and everything present is allowed by [:>= 3]
    
Creates a new Methodical multimethod named by a Var. Usage of this macro mimics usage of [[clojure.core/defmulti]],
and it can be used as a drop-in replacement; it does, however, support a larger set of options. Note the dispatch-fn
is optional (if omitted, then identity will be used). In addition to the usual `:default` and `:hierarchy` options,
you many specify:

* `:combo` - The method combination to use for this multimethods. Method combinations define how multiple applicable
   methods are combined; which auxiliary methods, e.g. `:before` or `:after` methods, are supported; and whether other
   advanced facilities, such as `next-method`, are available. There are over a dozen method combinations that ship as
   part of Methodical; many are inspired by their equivalents in the Common Lisp Object System. The default method
   combination is the thread-last method combination.

* `:dispatcher` - The dispatcher handles dispatch values when invoking a multimethod, and whether one dispatch value
   (and thus, whether its corresponding method) is considered to be more-specific or otherwise preferred over another
   dispatch value. The default dispatcher largely mimics the behavior of the Clojure dispatcher, using a single
   hierarchy augmented by a `prefers` table to control dispatch, with one big improvement: when dispatching on
   multiple values, it supports default methods that specialize on some args and use the default for others.
   (e.g. `[String :default]`)

   Note that the `:hierarchy`, `:default-value` and the positional `dispatch-fn` are provided as conveniences for
   creating a default dispatcher; if you pass a `:dispatcher` arg instead, those arguments are not required and will
   be ignored.

*  `:cache` - controls caching behavior for effective methods. The default simple cache mimics the behavior of vanilla
    Clojure multimethods.

*  `:method-table` - maintains tables of dispatch value -> primary method and auxiliary method qualifier -> dispatch
   value -> methods. The default implementation is a pair of simple maps.

The above options comprise the main constituent parts of a Methodical multimethod, and the majority of those parts
have several alternative implementations available in [[methodical.impl]]. Defining additional implementations is
straightforward as well: see [[methodical.interface]] for more details.

Other improvements over [[clojure.core/defmulti]]:

* Evaluating the form a second time (e.g., when reloading a namespace) will *not* redefine the multimethod, unless
  you have modified its form -- unlike vanilla Clojure multimethods, which need to be unmapped from the namespace to
  make such minor tweaks as changing the dispatch function.

Attribute map options:

`defmulti` supports a few additional options in its attributes map that will be used to validate `defmethod` forms
during macroexpansion time. These are meant to help the users of your multimethods use them correctly by catching
mistakes right away rather than waiting for them to pull their hair out later wondering why a method they added isn't
getting called.

* `:dispatch-value-spec` -- a spec for the `defmethod` dispatch value:

  ```clj
  (m/defmulti mf
    {:arglists '([x y]), :dispatch-value-spec (s/cat :x keyword?, :y int?)}
    (fn [x y] [x y]))

  (m/defmethod mf [:x 1]
    [x y]
    {:x x, :y y})
  ;; => ok

  (m/defmethod mf [:x]
    [x y]
    {:x x, :y y})
  ;; failed: Insufficient input in: [0] at: [:args-for-method-type :primary :dispatch-value :y] [:x]
  ```

  Note that this spec is applied to the unevaluated arguments at macroexpansion time, not the actual evaluated values.
  Note also that if you want to allow a `:default` method your spec will have to support it.

* `:defmethod-arities` -- a set of allowed/required arities that `defmethod` forms are allowed to have. `defmethod`
  forms must have arities that match *all* of the specified `:defmethod-arities`, and all of its arities must be
  allowed by `:defmethod-arities`:

  ```clj
  (m/defmulti ^:private mf
    {:arglists '([x]), :defmethod-arities #{1}}
    keyword)

  (m/defmethod mf :x [x] x)
  ;; => ok

  (m/defmethod mf :x ([x] x) ([x y] x y))
  ;; => error: {:arities {:disallowed #{2}}}

  (m/defmethod mf :x [x y] x y)
  ;; => error: {:arities {:required #{1}}}


  (m/defmethod mf :x [x y] x)
  ;; => error: {:arities {:required #{1 [:>= 3]}, :disallowed #{2}}}
  ```

  `:defmethod-arities` must be a set of either integers or `[:> n]` forms to represent arities with `&` rest
  arguments, e.g. `[:>= 3]` to mean an arity of three *or-more* arguments:

  ```clj
  ;; methods must both a 1-arity and a 3+-arity
  (m/defmulti ^:private mf
    {:arglists '([x] [x y z & more]), :defmethod-arities #{1 [:>= 3]}}
    keyword)

  (m/defmethod mf :x ([x] x) ([x y z & more] x))
  ;; => ok
  ```

  When rest-argument arities are used, Methodical is smart enough to allow them when appropriate even if they do not
  specifically match an arity specified in `:defmethod-arities`:

  ```clj
  (m/defmulti ^:private mf
    {:arglists '([x y z & more]), :defmethod-arities #{[:>= 3]}}
    keyword)

  (m/defmethod mf :x
    ([a b c] x)
    ([a b c d] x)
    ([a b c d & more] x))
  ;; => ok, because everything required by [:>= 3] is covered, and everything present is allowed by [:>= 3]
  ```
sourceraw docstring

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close