Ответ 1
Общее введение:
Символы в любом Lisp используются как идентификаторы. Если вы собираетесь ссылаться на значение переменной, скажем, вам нужно иметь способ ее именовать; для каких символов. Помните, что код Lisp преобразуется во время чтения в структуры данных Lisp; Идентификаторы также должны быть представлены некоторой структурой данных, и это, оказывается, символ. При встрече с символом eval
отправляется на какую-либо операцию поиска имени.
Переходя от общих черт Lisp к Clojure деталям, поведение Clojure eval/compiler заключается в том, что, столкнувшись с символом, он принимает это имя как для локальной, так и для локальной переменной let
параметр функции или имя записи в пространстве имен. На самом деле в первой емкости могут использоваться только символы, не содержащие пространства имен (что означает символы формы foo
, а не some-namespace/foo
).
Пример с грубым наброском:
Для символа foo
, не относящегося к пространству имен, если найден параметр привязки/функции let
имени foo
, символ оценивает его значение. Если это не так, символ преобразуется в форму *ns*/foo
(*ns*
обозначает текущее пространство имен), и делается попытка найти запись с текущей встречей в *ns*
; если есть такая запись, ее значение возвращается, если нет, генерируется исключение.
Обратите внимание, что символ, подобный identity
, если он используется в пространстве имен quux
, будет разрешен до clojure.core/identity
через промежуточный этап, на котором обнаружена запись в quux/identity
; это обычно относится к clojure.core/identity
. Эта деталь реализации, о которой вы не интуитивно поняли, не учитывает, но которую я не могу не упомянуть при попытке объяснить это.
В соответствующем пространстве имен будет отображаться символ, который уже имеет пространство имен (что-то вроде zip/root
в пространстве имен, в котором refer
до clojure.zip
без use
").
Там добавлена сложность с макросами (которые могут возникать только в позиции оператора), но это не очень важно для поведения самих символов.
Vars vs Symbols:
Обратите внимание, что в Clojure символы не являются местами хранения - Vars. Поэтому, когда я говорю в приведенном выше примере, что символ просматривается в пространстве имен, я имею в виду, что eval
просматривает Var, названный символом, разрешенным к его форме с именем, и затем принимает значение этого. Специальная форма var
(часто сокращается до #'
) изменяет это поведение, так что сам объект Var возвращается. Механика разрешения от символа до варра не изменяется.
Заключительные замечания:
Обратите внимание, что все это означает, что символы только "привязаны" к объектам в том смысле, что eval
при оценке символа продолжает искать какой-то дополнительный объект. Сам символ не имеет "слота" или "поля" для привязки объекта к нему; любое впечатление о том, что символ привязан к некоторому объекту, обусловлен работой eval
. Это характеристика Clojure, так как в некоторых символах Лисса сами действуют как места хранения.
Наконец, можно использовать обычный механизм цитирования, чтобы предотвратить оценку символа: в 'foo
символ foo
не будет оцениваться (так что никакого поиска имени не будет выполнено); он будет возвращен без изменений.
В ответ на комментарий OP: попробуйте это для удовольствия:
(defmacro symbol?? [x]
(if (symbol? x)
true
false))
(def s 1)
(symbol? s)
; => false
(symbol?? s)
; => true
(symbol? 's)
; => true
(symbol?? 's)
; => false
Последнее объяснение: 's
является сокращением для (quote s)
; это структура списка, а не символ. Макрос работает с аргументами, переданными непосредственно, без оценки; поэтому в (symbol?? 's)
он фактически видит структуру списка (quote s)
, которая, конечно же, не является символом, хотя, если она передана в eval
, она будет оценивать один.