Преамбула
Я искал исходный код в clojure.core
без особой причины.
Я начал читать defmacro ns
, вот сокращенный источник:
(defmacro ns
"...docstring..."
{:arglists '([name docstring? attr-map? references*])
:added "1.0"}
[name & references]
(let [...
; Argument processing here.
name-metadata (meta name)]
`(do
(clojure.core/in-ns '~name)
~@(when name-metadata
`((.resetMeta (clojure.lang.Namespace/find '~name) ~name-metadata)))
(with-loading-context
~@(when gen-class-call (list gen-class-call))
~@(when (and (not= name 'clojure.core) (not-any? #(= :refer-clojure (first %)) references))
`((clojure.core/refer '~'clojure.core)))
~@(map process-reference references))
(if (.equals '~name 'clojure.core)
nil
(do (dosync (commute @#'*loaded-libs* conj '~name)) nil)))))
Глядя ближе
И затем, пытаясь прочитать это, я увидел некоторые странные макросы, в частности, мы можем посмотреть на:
~@(when name-metadata
`((.resetMeta (clojure.lang.Namespace/find '~name) ~name-metadata)))
clojure.core
версия
Вот отдельное рабочее извлечение из макроса:
(let [name-metadata 'name-metadata
name 'name]
`(do
~@(when name-metadata
`((.resetMeta (clojure.lang.Namespace/find '~name) ~name-metadata)))))
=> (do (.resetMeta (clojure.lang.Namespace/find (quote name)) name-metadata))
Когда я запустил это, я не мог не задаться вопросом, почему в точке `((.resetMeta
есть двойной список.
Моя версия
Я обнаружил, что просто удалив сплайсинг без кавычек (~@
), двойной список не нужен. Вот рабочий автономный пример:
(let [name-metadata 'name-metadata
name 'name]
`(do
~(when name-metadata
`(.resetMeta (clojure.lang.Namespace/find '~name) ~name-metadata))))
=> (do (.resetMeta (clojure.lang.Namespace/find (quote name)) name-metadata))
Мой вопрос
Таким образом, почему clojure.core выбирает этот, казалось бы, посторонний способ действий?
Мои мысли
Это артефакт конвенции?
Существуют ли другие подобные случаи, когда это используется более сложными способами?