Documentation of records in clojure
Asked Answered
A

2

15

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?

  • How should records be documented?
  • Are records an appropriate solution here?
Automotive answered 19/10, 2014 at 19:5 Comment(4)
If you read further down in that thread, you will see that adding a :doc key to the record's metadata won't work. Note that you can add a doc string to a protocol.Poetess
but this stack overflow answer recommends against writing protocols that only get implemented by one record, which is probably what would happen.Automotive
One solution is a library like prismatic/schema, that allows you to specify the type of data you will accept, also allowing verification of the arguments provided.Elma
The temporary solution is to precede the defrecord with a comment starting with two semicolons, which works for marg but not in the replAutomotive
B
9

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.

Bowlin answered 18/7, 2017 at 19:12 Comment(0)
A
0

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.

Abrade answered 16/9, 2020 at 11:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.