Символы в Clojure - PullRequest
       18

Символы в Clojure

30 голосов
/ 23 февраля 2010

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

Ответы [ 2 ]

56 голосов
/ 23 февраля 2010

Общее вступление:

Символы в любом Лиспе используются в качестве идентификаторов. Если вы собираетесь ссылаться на значение переменной, скажем, вам нужен способ присвоения ей имени; для этого и нужны символы. Помните, что весь код 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 она будет иметь значение, равное единице.

23 голосов
/ 25 февраля 2010

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

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

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

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...