Question

I want to add optional docstrings to my def* macros. For example:

(defmacro defhtml
  "Macro to avoid backtick unquote[splicing] in html vectors.
   TODO: Add optional docstring."
  [name args & body]
  `(defn ~name ~args (html ~@body)))

;; Working defhtml
(defhtml include-css [href]
  [:link {:href href :rel "stylesheet"}])

I would like:

(defhtml include-css
  "My optional docstring here."
  [:link {:href href :rel "stylesheet"}])

I figure there should be some common idiom for this.

Was it helpful?

Solution

You'll need to decision off of whether or not the second argument to your macro is a doc-string (you can test if it's a string). Clojure macros are Clojure, so you can perform any logic or manipulations on the forms passed to the macro that you want to. This should be close to if not exactly what you're after:

(defmacro defhtml [name & args]
  (cond
    ;; doc-string?
    (string? (first args))
    (let [[doc-string args-list & body] args]
      `(defn ~name ~doc-string ~args-list (html ~@body)))

    :no-doc-string
    (let [[args-list & body] args]
      `(defn ~name ~(format "HTML Generator %s" name) ~args-list (html ~@body)))))

That should produce the macro expansions you're after:

(defhtml include-css [href]
  [:link {:href href :rel "stylesheet"}])

produces:

(defn include-css
    "HTML Generator include-css"
  [href]
  (html [:link {:href href, :rel "stylesheet"}]))

while:

(defhtml include-css
  "Standard css includes fory my site"
  [href]
  [:link {:href href :rel "stylesheet"}])

produces:

(defn include-css
    "Standard css includes fory my site"
  [href]
  (html [:link {:href href, :rel "stylesheet"}]))

OTHER TIPS

defn and defmacro already support optional docstrings, so if your def* macro expands to a call to one of these, you may not need to include any string? checks in your own code. This is the case with defhtml, which can be implemented thus:

;; also adding an optional attribute map parameter, because why not?
(defmacro defhtml [name doc? attr-map? params & body]
  `(defn ~name ~doc? ~attr-map? ~params (html ~@body)))

;; equivalent, perhaps prettier:
(defmacro defhtml [name doc? attr-map? params & body]
  `(defn ~@[name doc? attr-map? params] (html ~@body)))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top