Ответ 1
Проблема здесь тонкая и, возможно, трудно получить, не вникая немного в макросы.
Макросы манипулируют синтаксисом так же, как функции манипулируют значениями. На самом деле макросы - это просто функции с крючком, которые заставляют их оцениваться во время компиляции. Они передаются литералом данных, который вы видите в исходном коде, и оцениваются сверху вниз. Позвольте сделать функцию и макрос, который имеет одно и то же тело, чтобы вы могли видеть разницу:
(defmacro print-args-m [& args]
(print "Your args:")
(prn args))
(defn print-args-f [& args]
(print "Your args:")
(prn args))
(print-args-m (+ 1 2) (str "hello" " sir!"))
; Your args: ((+ 1 2) (str "hello" " sir!"))
(print-args-f (+ 1 2) (str "hello" " sir!"))
; Your args: (3 "hello sir!")
Макросы заменяются на их возвращаемое значение. Вы можете проверить этот процесс с помощью macroexpand
(defmacro defmap [sym & args]
`(def ~sym (hash-map [email protected]))) ; I won't explain these crazy symbols here.
; There are plenty of good tutorials around
(macroexpand
'(defmap people
"Steve" {:age 53, :gender :male}
"Agnes" {:age 7, :gender :female}))
; (def people
; (clojure.core/hash-map
; "Steve" {:age 53, :gender :male}
; "Agnes" {:age 7, :gender :female}))
В этот момент я должен, вероятно, объяснить, что '
вызывает следующую форму: quote
d. Это означает, что компилятор прочитает форму, но не выполнит ее или не попытается разрешить символы и т.д. т.е. 'conj
оценивается символом, а conj
- функцией. (eval 'conj)
эквивалентно (eval (quote conj))
эквивалентно conj
.
С учетом этого, знайте, что вы не можете разрешить символ как пространство имен до тех пор, пока оно не будет волшебным образом импортировано в ваше пространство имен каким-то образом. Это то, что делает функция require
. Он принимает символы и находит пространства имён, которым они соответствуют, делая их доступными в текущем пространстве имен.
Посмотрите, что макрос ns
расширяется до:
(macroexpand
'(ns sample.core
(:require clojure.set clojure.string)))
; (do
; (clojure.core/in-ns 'sample.core)
; (clojure.core/with-loading-context
; (clojure.core/refer 'clojure.core)
; (clojure.core/require 'clojure.set 'clojure.string)))
Посмотрите, как он цитирует символы clojure.set
и clojure.string
для нас? Как удобно! Но какая сделка, когда вы используете require
вместо :require
?
(macroexpand
'(ns sample.core
(require clojure.set clojure.string)))
; (do
; (clojure.core/in-ns 'sample.core)
; (clojure.core/with-loading-context
; (clojure.core/refer 'clojure.core)
; (clojure.core/require 'clojure.set 'clojure.string)))
Кажется, что тот, кто написал макрос ns
, был достаточно хорош, чтобы позволить нам делать это в обоих направлениях, так как этот результат точно такой же, как и раньше. Neato!
edit: tvachon прав только при использовании :require
, поскольку это единственная официально поддерживаемая форма
Но какова сделка с скобками?
(macroexpand
'(ns sample.core
(:require [clojure.set]
[clojure.string])))
; (do
; (clojure.core/in-ns 'sample.core)
; (clojure.core/with-loading-context
; (clojure.core/refer 'clojure.core)
; (clojure.core/require '[clojure.set] '[clojure.string])))
Оказывается, они также цитируются, как и мы, если бы мы писали автономные вызовы require
.
Также оказывается, что ns
не заботит, будем ли мы давать ему списки (parens) или векторы (скобки) для работы. Он просто рассматривает аргументы как последовательности вещей. Например, это работает:
(ns sample.core
[:gen-class]
[:require [clojure.set]
[clojure.string]])
require
, как указано в комментариях к амаллою, имеет разную семантику для векторов и списков, поэтому не смешивайте их up!
Наконец, почему не работает следующее?
(ns sample.core
(:require 'clojure.string 'clojure.test))
Хорошо, так как ns
содержит наши цитаты для нас, эти символы дважды цитируются, что семантически отличается от цитирования только один раз и является также безумным безумием.
conj ; => #<core$conj [email protected]>
'conj ; => conj
''conj ; => (quote conj)
'''conj ; => (quote (quote conj))
Надеюсь, это поможет, и я определенно рекомендую научиться писать макросы. Они супер веселья.