Символы в Clojure

Каково объяснение для Символов в Clojure, который будет связан с основным объектом, и имейте дополнительное отдельное значение? Возможно, что-то элементарное я отсутствую, но был бы великим, если кто-то мог бы указать Почему.

30
задан Arun R 23 February 2010 в 17:38
поделиться

2 ответа

Общее введение:

Символы в любом Лиспе используются как идентификаторы. Если вы собираетесь ссылаться, скажем, на значение переменной, вам нужно иметь способ назвать ее; для этого и нужны символы. Помните, что весь код Лиспа при чтении транслируется в структуры данных Лиспа; идентификаторы также должны быть представлены некоторой структурой данных, и она оказывается символом. Встретив символ, eval выполняет своего рода операцию "поиска имени".

Переходя от общих черт Lisp к особенностям Clojure, поведение eval / компилятора Clojure заключается в том, что при встрече с символом он принимает его за имя либо let-введенной локальной переменной или параметра функции либо за имя записи в пространстве имен. На самом деле в первом качестве могут использоваться только символы, не являющиеся квалификаторами пространства имен (имеются в виду символы вида foo, а не some-namespace/foo).

Пример:

Для символа foo, не имеющего квалификации в пространстве имен, если найдена привязка let / параметр функции с именем foo, то символ оценивается по его значению. Если нет, то символ преобразуется к форме *ns*/foo (*ns* обозначает текущее пространство имен) и делается попытка найти соответствующую запись в *ns*; если такая запись есть, то возвращается ее значение, если нет, то возникает исключение.

Обратите внимание, что символ типа identity, используемый в пространстве имен quux, будет разрешен в clojure.core/identity через промежуточный шаг, на котором будет найдена запись в quux/identity; обычно это refer в clojure.core/identity. Это деталь реализации, о которой не задумываются при интуитивном кодировании, но которую я не могу не упомянуть при попытке объяснить это.

Символ, который уже имеет квалификацию пространства имен (что-то вроде zip/root в пространстве имен, которое ссылается на clojure.zip без использования), будет искаться в соответствующем пространстве имен.

Есть некоторая дополнительная сложность с макросами (которые могут встречаться только в позиции оператора), но это не имеет отношения к поведению самих символов.

Vars vs Symbols:

Обратите внимание, что в Clojure символы сами по себе не являются местами хранения - это Vars. Поэтому, когда я говорю выше, что символ ищется в пространстве имен, я имею в виду, что eval ищет Var, названный символом, преобразованный в его форму, заданную в пространстве имен, и затем берет его значение. Специальная форма var (часто сокращаемая до #') изменяет это поведение так, что возвращается сам объект Var. Механика разрешения перехода от символа к Var остается неизменной.

Заключительные замечания:

Заметим, что все это означает, что символы "привязаны" к объектам только в том смысле, что eval, оценивая символ, продолжает поиск какого-то другого объекта. Сам символ не имеет "слота" или "поля" для привязки к нему объекта; любое впечатление, что символ "привязан" к какому-то объекту, обусловлено работой eval. Это особенность Clojure, поскольку в некоторых Lisps символы сами выступают в качестве мест хранения.

Наконец, можно использовать обычный механизм цитирования для предотвращения оценки символа: в '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 она будет оценена как символ.

56
ответ дан 27 November 2019 в 23:17
поделиться

Здесь может возникнуть некоторая путаница из-за различного использования термина "символ" в Common Lisp и в Clojure.

В Common Lisp "символ" - это место в памяти, место, где могут храниться данные. Значение" символа - это данные, хранящиеся в этом месте памяти.

В Clojure "символ" - это просто имя. Он не имеет значения.

Когда компилятор Clojure встречает символ, он пытается разрешить его как

  1. имя класса Java (если символ содержит точку)
  2. локальный (как в "let" или параметрах функции)
  3. Var в текущем пространстве имен
  4. Var, переданный из другого пространства имен

Var, как заметил один из предыдущих авторов, представляет собой место хранения.

Есть веские причины, по которым Clojure отделяет Var от Символов. Во-первых, это позволяет избежать раздражения от автоматически встраиваемых символов Common Lisp, которые могут "загрязнить" пакет нежелательными символами.

Во-вторых, переменные Clojure имеют особую семантику в отношении параллелизма. Вар имеет ровно одну "корневую привязку", видимую всем потокам. (Когда вы набираете "def", вы устанавливаете корневую привязку Var.) Изменения Var, сделанные в потоке (с помощью "set!" или "binding"), видны только этому потоку и его дочерним элементам.

25
ответ дан 27 November 2019 в 23:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: