Search code examples
clojuremetadatamultimethod

How to set and get multimethod metadata in clojure?


I'm using multimethods to parse command line commands and their arguments.

(defmulti run (fn [command args] command))

(defmethod run :default
  [& _]
  ...)

^{:args "[command]"}
(defmethod run "help"
  [_ & [args]]
  "Display command list or help for a given command"
  ...)

^{:args ""}
(defmethod run "version"
  [_ & [args]]
  "Print program's version"
  ...)

(defn -main
  [& args]
  (run (first args)
    (next args)))

When I try to access the metadata, for a specific method, clojure returns nil:

(meta ((methods run) "help"))

Solution

  • There's no such possibility. The first reason (straightforward one) is that defmethod doesn't provide an ability to set metadata for a particular method (only defmulti allows that, but only for the whole multimethod). Second reason is that multimethod is essentially a single function, just with multiple "variants" of execution, each of which fires depending on passed parameters. Rougly speaking, from caller point of view, there's no particular difference between functions f1 and f2 defined below:

    (defmulti f1 (fn [x] x))
    
    (defmethod f1 :foo [x]
      ...)
    
    (defmethod f1 :bar [x]
      ...)
    
    (defmethod f1 :baz [x]
      ...)
    
    (defn f2 [x]
      (case x
        :foo ...
        :bar ...
        :baz ...))
    

    Personally, I'd consider depending on whether particular function is multimethod or ordinary function as relying on implementation details. Also if you need to explicitly document each method of multimehod, you should consider replacing each method with ordinary function and don't use multimethods at all.