Документация записей в clojure

Ранее у меня был api, в котором было множество функций, все из которых ожидали карту в очень конкретном формате. Когда дело дошло до документирования этого API, я обнаружил, что в docstrings каждой из этих функций я повторял "Карта, с которой эта функция вызывается, должна иметь такой и такой формат, и это поле карты означает такое-то."

Итак, я подумал, что для этих функций будет лучше взять запись, и я мог бы просто документировать запись. Однако, как представляется, невозможно документировать записи, по крайней мере, интерпретироваться либо макросом doc, либо маргинальным.

Предлагаемое решение здесь означает "просто добавьте ключ: doc в метаданные записи".

Я попробовал (defrecord ^{:doc "Here is some documentation"} MyRecord [field1 field2]), но макрорасширение это говорит о том, что это не имеет никакого эффекта. Также defrecord возвращает экземпляр java.lang.class, который не реализует IMeta, поэтому я не уверен, что мы можем дать ему метаданные?

  • Как документировать документы?
  • Здесь записано подходящее решение?

Ответ 1

TL; DR: К сожалению, вы не можете.

Из документов:

Символы и коллекции поддерживают метаданные

Когда вы используете defrecord вы фактически создаете класс Java. Поскольку классы не являются ни символами, ни записями Clojure, вы не можете добавлять к ним документацию.

Более подробное объяснение

Следующий сеанс REPL показывает, почему невозможно добавлять метаданные в записи.

user=> (defrecord A [a b])
#<[email protected] user.A>
user=> (meta A)  ;; <= A contains no metadata
nil  

Здесь важно отметить, что A - это обычный Java-класс. Если вы попытаетесь установить метаданные для A, вы получите интересную ошибку

user=> (with-meta A {:doc "Hello"}) 

ClassCastException java.lang.Class cannot be cast to clojure.lang.IObj

Судя по всему с-мета ожидает clojure.lang.IObj. Поскольку java.lang.Class - это конструкция Java-land, он явно ничего не знает о clojure.lang.IObj.

Давайте теперь посмотрим на исходный код для 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))))

Как вы можете видеть, этот метод ожидает, что у x будет объект withMeta, которого нет в записях.