Преамбула
Я просматривал исходный код в 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 выбирает этот, казалось бы, посторонний способ ведения дел?
Мои собственные мысли
Это артефакт условности? Есть ли другие подобные случаи, когда это используется более сложными способами?