In the project I'm working on we often define custom defsomething
-style macros for different purposes to hide boilerplate. One example is defhook
which helps to define a hook handler for an event. Here's a simplified version of it (the actual version has more parameters and does some non-trivial things in defmethod
, but that's irrelevant to my question):
(defmulti handle-hook
"This multimethod is called when an event was fired."
(fn [event context] event))
(defmacro defhook
"Define a hook for an event."
[event docstring & more]
`(let [body# (fn ~@more)]
(defmethod handle-hook ~event [event# context#]
(body# context#))))
(defhook "EntryDeleted"
"Hook called on entry deletion."
[context]
(log-deletion (:EntryID context)))
The main problem I have with this code is that defmethod
does not support docstring, so I can't use the one for "EntryDeleted"
in REPL or for automatic documentation generation. The last one is important for the project: there are defhook
s and defhandler
s that are exposed as external API and currently we have to maintain documentation separately (and manually).
So the simplest question is "how to attach docstring to a defmethod
"?.
And the deeper one would be "how to attach/generate documentation for custom defsomething
macros?"
If some of the existing tools for documentation generation supported this feature it would be great! Yet, neither of Marginalia, Codox or Autodoc seem to support something like that.
How to attach/generate documentation for custom
defsomething
macros?
Since docstrings are attached to vars, you'd generally have defsomething
macros expand into a more primitive def
form, e.g. defn
, def
. Then you just arrange for your defsomething
's docstring to be attached to the underlying var.
How to attach docstring to a
defmethod
?
This is a special case - defmethod
is not defining a new var; it's calling a Java method on a Java object. On the other hand, defmulti
does create a var. One idea would be to extend the multi-function's docstring with the dispatch value and associated description. For example,
(defn append-hook-doc! [event docstring]
(let [hook-doc (str event " - " docstring)]
(alter-meta! #'handle-hook
(fn [m]
(update-in m [:doc] #(str % "\n\t" hook-doc))))))
...
(doc handle-hook)
-------------------------
user/handle-hook
This multimethod is called when an event was fired.
EntryDeleted - Hook called on entry deletion.
As the !
indicates, this form has a side-effect: multiple evaluations of a defining form that calls this will result in duplicate lines in #'handle-hook
's docstring. You might avoid this by stashing some extra metadata in #'handle-hook
to use as a marker for whether or not the doc has already been appended. Alternatively, you might stash the docstrings elsewhere and patch it all together in some auxiliary step, e.g. by delaying the expansion of (defmulti handle-hook ...
until you have all the docstrings (although, this breaks the open extension of multi-methods wrt docstrings).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With