Reputation: 4060
I previously had an api which had a number of functions in it, all of which expected a map in a very particular format. When it came to documenting this API, I found that in the docstrings of each of these functions I was repeating "The map with which this function is called must be of such and such a format, and this field of the map means such and such."
So I thought it would be better for those functions to take a record, and that I could just document the record instead. However it doesn't seem to be possible to document records, at least in any way interpreted either by the doc
macro or Marginalia.
A solution suggested here is "just add a :doc key in the record's meta".
I tried (defrecord ^{:doc "Here is some documentation"} MyRecord [field1 field2])
but macroexpanding this suggests it doesn't have any effect. Also defrecord
returns an instance of java.lang.class
which doesn't implement IMeta so I'm not sure we can give it metadata?
Upvotes: 15
Views: 2506
Reputation: 2301
You can't put a docstring on the record. But if you really want to, then effectively you can.
If you want users reading the code to know your intent then you can add a comment to the code.
If you want users creating an instance of your record to have access to your docstring through tooling then you can modify the created constructor function metadata. e.g.:
(let [docstring "The string-representation *MUST* be ISO8601."
arglists '([string-representation millis-since-epoch])
arglists-map '([{:keys [:string-representation :millis-since-epoch]}])]
(defrecord Timestamp [string-representation millis-since-epoch])
(alter-meta! #'->Timestamp assoc :doc docstring)
(alter-meta! #'->Timestamp assoc :arglists arglists)
(alter-meta! #'map->Timestamp assoc :doc docstring)
(alter-meta! #'map->Timestamp assoc :arglists arglists-map))
For me using a REPL in Cursive I see the argslist popup when I ask for the 'parameter info' and the docstring when I ask for 'quick documentation'.
Alternatively a better approach might be to provide your own constructor functions with standard docstrings.
Upvotes: 0
Reputation: 7154
TL;DR: Unfortunately you can't.
From the docs:
Symbols and collections support metadata
When you use defrecord
you are actually creating a java class. Since classes are neither symbols nor Clojure records, you cannot append documentation to them.
More Detailed Explanation
The following REPL session shows why its not possible to append metadata to records.
user=> (defrecord A [a b])
#<Class@61f53f0e user.A>
user=> (meta A) ;; <= A contains no metadata
nil
The important bit to notice here is that A is a regular java class. If you try to set the metadata for A you will get an interesting error
user=> (with-meta A {:doc "Hello"})
ClassCastException java.lang.Class cannot be cast to clojure.lang.IObj
Apparently with-meta expects a clojure.lang.IObj
. Since java.lang.Class
is a a Java-land construct, it clearly knows nothing of clojure.lang.IObj
.
Let's take a look now at the source code for with-meta
user=> (source with-meta)
(def
^{:arglists '([^clojure.lang.IObj obj m])
:doc "Returns an object of the same type and value as obj, with
map m as its metadata."
:added "1.0"
:static true}
with-meta (fn ^:static with-meta [^clojure.lang.IObj x m]
(. x (withMeta m))))
As you can see, this method expects x
to have a withMeta
object, which records clearly don't have.
Upvotes: 9