Подтвердить что ты не робот

Символы в Clojure

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

4b9b3361

Ответ 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, она будет оценивать один.

Ответ 2

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

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

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

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

  • имя класса Java (если символ содержит точку)
  • локальный (как с параметрами "let" или function)
  • a Var в текущем пространстве имен
  • a Var, относящийся к другому пространству имен

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

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

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