Общее вступление:
Символы в любом Лиспе используются в качестве идентификаторов. Если вы собираетесь ссылаться на значение переменной, скажем, вам нужен способ присвоения ей имени; для этого и нужны символы. Помните, что весь код Lisp транслируется во время чтения в структуры данных Lisp; идентификаторы также должны быть представлены некоторой структурой данных, и это происходит с символом. При обнаружении символа eval
отправляет какую-то операцию «поиска имени».
Переходя от общепринятых Lisp к деталям Clojure, поведение eval / compiler Clojure заключается в том, что при встрече с символом оно принимает имя либо для введенной 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
с use
без него) будет найден в соответствующем пространстве имен.
Существует некоторая сложность с макросами (которая может возникать только в позиции оператора), но на самом деле это не имеет отношения к поведению самих символов.
Vars vs Символы:
Обратите внимание, что в Clojure символы сами по себе не являются местами хранения - Варианты есть. Поэтому, когда я говорю выше, что символ ищется в пространстве имен, я имею в виду, что eval
ищет Var, названный символом, преобразованным в его форму, определенную для пространства имен, и затем принимает значение этого. Специальная форма var
(часто сокращенно #'
) изменяет это поведение так, что возвращается сам объект Var. Механика разрешения символа-к-ва, тем не менее, не изменилась.
Заключительные замечания:
Обратите внимание, что все это означает, что символы «привязаны» только к объектам в том смысле, что eval
при оценке символа продолжает искать какой-то дополнительный объект. Сам символ не имеет «слота» или «поля» для объекта, который будет привязан к нему; любое впечатление, что символ «привязан» к какому-либо объекту, связано с работой eval
. Это характеристика Clojure, так как в некоторых Лиспах символы сами по себе действуют как места хранения.
Наконец, можно использовать обычный механизм цитирования, чтобы предотвратить оценку символа: в 'foo
символ foo
не будет оцениваться (поэтому поиск имени любого рода не будет выполнен); вместо этого он будет возвращен без изменений.
В ответ на комментарий ОП: Попробуйте это в шутку:
(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
она будет иметь значение, равное единице.