Разница между символами и переменными в Clojure - PullRequest
40 голосов
/ 02 февраля 2012

Меня всегда немного смущают символы и перемены в Clojure. Например, можно ли с уверенностью сказать, что + - это символ, который используется для обозначения переменной var, а эта переменная указывает на значение, являющееся функцией, которая может добавлять числа?

Так что же происходит, шаг за шагом, когда я просто ввожу "+" в REPL?

  1. Символ становится квалифицированным для пространства имен, в данном случае clojure.core
  2. Тогда в некоторой таблице символов есть информация, которая + относится к переменной
  3. Когда этот var вычисляется, результатом является значение функции?

Ответы [ 3 ]

61 голосов
/ 02 февраля 2012

Есть символ +, о котором вы можете поговорить, цитируя его:

user=> '+
+
user=> (class '+)
clojure.lang.Symbol
user=> (resolve '+)
#'clojure.core/+

Таким образом, он разрешается до # '+, который является Var:

user=> (class #'+)
clojure.lang.Var

Var ссылается на объект функции:

user=> (deref #'+)
#<core$_PLUS_ clojure.core$_PLUS_@55a7b0bf>
user=> @#'+
#<core$_PLUS_ clojure.core$_PLUS_@55a7b0bf>

(Знак @ - это просто сокращение для разыменования.) Конечно, обычный способ добраться до функции - не заключать в кавычки символ:

user=> +
#<core$_PLUS_ clojure.core$_PLUS_@55a7b0bf>

Обратите внимание, что лексические привязки - это другой механизм, и они могут скрывать Vars, но вы можете обойти их, явно ссылаясь на Var:

user=> (let [+ -] [(+ 1 2) (@#'+ 1 2)])
[-1 3]

В последнем примере дереф можно даже опустить:

user=> (let [+ -] [(+ 1 2) (#'+ 1 2)])
[-1 3]

Это потому, что Var реализует IFn (интерфейс для функций Clojure), вызывая deref для себя, передавая результат в IFn и делегируя вызов функции этому.

Механизм видимости, используемый при определении частных функций с помощью defn, основан на метаданных символа. Вы можете обойти это, обращаясь непосредственно к Var, как указано выше:

user=> (ns foo)
nil
foo=> (defn- private-function [] :secret)
#'foo/private-function
foo=> (in-ns 'user)
#<Namespace user>
user=> (foo/private-function)
java.lang.IllegalStateException: var: #'foo/private-function is not public (NO_SOURCE_FILE:36)
user=> (#'foo/private-function)
:secret
7 голосов
/ 02 февраля 2012

См. документацию для пространств имен :

Пространства имен представляют собой сопоставления простых (неквалифицированных) символов с переменными и / или классами.Vars могут быть интернированы в пространстве имен, используя def или любой из его вариантов, и в этом случае они имеют простой символ для имени и ссылку на содержащееся в них пространство имен, а пространство имен отображает этот символ в один и тот же var.Пространство имен также может содержать сопоставления символов с переменными, интернированными в других пространствах имен с помощью ссылки или использования, или с символов на объекты класса с помощью импорта.

Таким образом, в основном ваши шаги 1 и 2 объединены:пространства имен являются таблицами символов.

А что касается шага 3: мне нравится определение переменных, которые представляют собой комбинации имен значений.Символ - это имя переменной, и при оценке его значение будет равно.

4 голосов
/ 02 мая 2017

Этот ответ не сильно отличается от других, он просто не предполагает, что вы изначально хотели изучить несколько новых функций и концепций, просто чтобы понять, что происходит:

  1. +символ, расположенный в clojure.core, который по умолчанию доступен для вашего кода.
  2. При использовании в вашем коде без каких-либо очень сложных намерений, таких как цитирование его или выяснение его класса - clojure будет искать Var, на который он указывает.
  3. Если этот Var является функцией, когда + используется в заголовке списка, clojure попытается вызвать эту функцию (NullPointerException, если этот Var не указывает на функцию).Если эта функция передана в качестве аргумента другой функции, эта функция может сделать то же самое, чтобы вызвать ее.Вот как работает вызов функции.

дополнительные комментарии для округления:

Большинство или все языки используют таблицы символов.Будучи несколько динамичным языком, Clojure использует этот дополнительный уровень косвенности (Symbol → Var → function, а не только Symbol → function), так что динамическое переписывание, какая функция связана с каким символом, - более выполнимо и элегантно, и иногда этоисточник любопытства для начинающих.

Поскольку другие ответы несколько чрезмерно подчеркнуты, вы можете иначе выполнить причудливые вещи , например, цитировать его ('+), чтобы избежать его оценки, или даже проверить его с помощью class и / илиresolve как будто вы заинтересованы в проверке, что это такое (class) или в каком пространстве имен оно находится (resolve).Вы также можете нажать на переменную, на которую он указывает, через var или #'.Обычно вы будете делать эти причудливые вещи, если будете писать макросы или если вы очень склонны к эксперименту, особенно при работе в репле;в зависимости от того, в каком стиле вы пишете свои макросы, вы можете на них довольно много цитировать.

и причудливую иллюстрацию для дополнительного исследователя:

Будучи несколько гибким языком, clojure разоблачаетAPI для принятия Symbol → Var → function иди сам.Как правило, вы никогда бы не сделали это для простого использования функции, потому что, очевидно, это было бы скучно и излишне, но это можно использовать здесь для иллюстрации процесса:

(deref (resolve '+))

А именно, символ сначала разрешаетсяк его Var, тогда то, на что указывает Var, достигнуто.Это просто иллюстрирует двухэтапный процесс достижения функции (Symbol → Var → function), который происходит за кулисами.Я надеюсь, что вы избежали чтения этой дополнительной части.


TL; DR

ответ на первоначальный вопрос просто: Да.

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