Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Documentation of records in clojure

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?
like image 608
George Simms Avatar asked Oct 19 '14 19:10

George Simms


1 Answers

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.

like image 166
fernandohur Avatar answered Nov 11 '22 05:11

fernandohur