clojure 2026-06-12

what's the intended way to do multi-line docstrings in clojure? i had something like this:

(defn format-date []
  "Format a date-like value with a java.time pattern, defaulting to yyyy-MM-dd.
    See [DateTimeFormatter patterns]()."
  ; ...
but that doesn't render properly because Clojure counts the leading spaces as part of the docstring. defn doesn't support multiple docstrings, and \ before the newline was just a syntax error.

docstrings go before the arglist

โž• 2

user=> (source transduce)
(defn transduce
  "reduce with a transformation of f (xf). If init is not
  supplied, (f) will be called to produce it. f should be a reducing
  step function that accepts both 1 and 2 arguments, if it accepts
  only 2 you can add the arity-1 with 'completing'. Returns the result
  of applying (the transformed) xf to init and the first item in coll,
  then applying xf to that result and the 2nd item, etc. If coll
  contains no items, returns init and f is not called. Note that
  certain transforms may inject or skip items."  {:added "1.7"}
  ([xform f coll] (transduce xform f (f) coll))
  ([xform f init coll]
     (let [f (xform f)
           ret (if (instance? clojure.lang.IReduceInit coll)
                 (.reduce ^clojure.lang.IReduceInit coll f init)
                 (clojure.core.protocols/coll-reduce coll f init))]
       (f ret))))
nil
user=> (doc transduce)
-------------------------
clojure.core/transduce
([xform f coll] [xform f init coll])
  reduce with a transformation of f (xf). If init is not
  supplied, (f) will be called to produce it. f should be a reducing
  step function that accepts both 1 and 2 arguments, if it accepts
  only 2 you can add the arity-1 with 'completing'. Returns the result
  of applying (the transformed) xf to init and the first item in coll,
  then applying xf to that result and the 2nd item, etc. If coll
  contains no items, returns init and f is not called. Note that
  certain transforms may inject or skip items.
nil
user=>

are you asking how each line in the docstring should be indented to have it formatted a certain way?

yes -- i'm trying to render it to html

right now it's often not valid markdown, either because someone uses <body> to indicate an argument named body, or because it's indented strangely and that messes things up

most docstrings are markdown-like but i'm not sure anyone's tried to do this in a rigorous way

i am indeed using a fork of quickdoc

it breaks on clojure.data.xml docstrings

haha. oh ๐Ÿ‘

i have some hacks to make it at least valid markdown, by treating three-space-indent as "probably not markdown", but now i have to format all my docstrings weirdly if i want [links]() to render

to my knowledge, there's no agreed standard for indentation in docstrings. just looking now, my employer's codebase sometimes indents the second line to the same position as the open-quote, sometimes with another space

hm, so, i'm not asking for a standard, i'm asking if there's a way for me in my own code to say "this docstring should have no leading whitespace" without formatting it this way:

(defn format-date
  "Format a date-like value with a java.time pattern, defaulting to yyyy-MM-dd.
See [DateTimeFormatter patterns]()."
  [] ; ...

I vaguely remember some experimental clojure-like dialect maybe throwing an error if lines in a string werenโ€™t indented after the open-quote, and then stripping the indentation like that

there is a sort of feedback cycle, doc was the first consumer of docstrings, so people write docstrings for it, and because people write docstrings for it, other docstring consumers need to support its conventions

it has shifted a little bit since so many people consume docstrings via 3rd party tools now, but the bias is strong

is doc a tool? do you have a link?

clojure.repl/doc

user=> (source doc)
(defmacro doc
  "Prints documentation for a var or special form given its name,
   or for a spec if given a keyword"
  {:added "1.0"}
  [name]
  (if-let [special-name ('{& fn catch try finally try} name)]
    `(#'print-doc (#'special-doc '~special-name))
    (cond
      (special-doc-map name) `(#'print-doc (#'special-doc '~name))
      (keyword? name) `(#'print-doc {:spec '~name :doc '~(spec/describe name)})
      (find-ns name) `(#'print-doc (#'namespace-doc (find-ns '~name)))
      (resolve name) `(#'print-doc (meta (var ~name))))))
nil
user=> `doc
clojure.repl/doc
user=>

hm ok, so its convention seems to be "the first line is indented to the same depth as the second line, and lines are printed literally after that"

if itโ€™s only for your own code, i would just warn if docstrings are improperly formatted to your conventions and then strip indentation from the html outputted docstrings

well the problem is that i need it to work for both my code and for clojure.data.xml

maybe i can just special-case that whole namespace in my quickdoc fork

apply different rules for each then?

๐Ÿ‘ 1

@jyn514 if you're using a fork of quickdoc: issues + fixes welcome ;)

most of the fork is because i'm doing evil things to document the APIs that Flower exposes in the SCI guest ๐Ÿ˜… not sure they make sense to upstream

using (sci/eval '(ns-public ...)) and stuff like that

@borkdude one thing that would be helpful would be adding a way for me to hand quickdoc a list of namespaces / vars / docs, and letting it choose the output formatting. not sure if that's something you're interested in though. (i can be more specific about the exact api if you give me a sec, i just have to reread the code)

I kind new to this, but i would try to assign the :doc meta directly

(!set *print-meta* true)
; true

(defn custom-formating [doc-string] (clojure.string/replace doc-string #"\s+" " "))
; ^{:arglists ([doc-string]), ...} #'user/custom-formating

(defn ^{:doc (custom-formating "a multiline idented
                                doc string for afunction")} a-fn [x])
; ^{:arglists ([x]), :doc "a multiline idented doc string for another function", ...} #'user/a-fn 

(doc a-fn)
; -------------------------
; user/a-fn
; ([x])
;   a multiline idented doc string for another function
; nil
In your case (custom-formating "") would probably read (no-leading-whitespace "")

maybe you could write a custom formatter-style hook that uses rewrite-clj to format docstrings the way you need (?)

as for "the intended way to do multi-line docstrings" (the original ask), if I don't have any strong opinions otherwise, I just follow what https://guide.clojure.style/#docstring-indentation says