Разница между символами и Vars в Clojure
Я всегда немного смущен символами и варами в Clojure.
Например, можно ли с уверенностью сказать, что символ + является символом, который используется для обозначения var, и этот var указывает на значение, которое является функцией, которая может добавлять числа?
Итак, что происходит, шаг за шагом, когда я просто вводил "+" в REPL?
- Символ получает квалификацию в пространстве имен, в этом случае Clojure.core
- Затем в некоторой таблице символов есть информация, которая + ссылается на var
- Когда этот var оценивается, результатом является значение функции?
Ответы
Ответ 1
Здесь есть символ +, о котором вы можете говорить, указав его:
user=> '+
+
user=> (class '+)
clojure.lang.Symbol
user=> (resolve '+)
#'clojure.core/+
Итак, он разрешает # '+, что является Var:
user=> (class #'+)
clojure.lang.Var
Вар ссылается на объект функции:
user=> (deref #'+)
#<core$_PLUS_ [email protected]>
user=> @#'+
#<core$_PLUS_ [email protected]>
(Знак @является просто сокращением для deref.) Конечно, обычный способ добраться до функции - не цитировать символ:
user=> +
#<core$_PLUS_ [email protected]>
Обратите внимание, что лексические привязки - это другой механизм, и они могут теневать Vars, но вы можете обойти их, явно ссылаясь на Var:
user=> (let [+ -] [(+ 1 2) (@#'+ 1 2)])
[-1 3]
В этом последнем примере deref можно даже оставить без внимания:
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
Ответ 2
Смотрите документацию для пространств имен:
Пространства имен - это отображения от простых (неквалифицированных) символов к Vars и/или классам. Vars может быть интернирован в пространстве имен, используя def или любой из его вариантов, и в этом случае у них есть простой символ для имени и ссылки на их содержащее пространство имен, а пространство имен отображает этот символ в тот же var. Пространство имен также может содержать сопоставления из символов в vars, интернированные в других пространствах имен, путем использования ссылки или использования или из символов в объекты класса с помощью импорта.
Итак, в основном ваши шаги 1 и 2 унифицированы: пространства имен являются таблицами символов.
И в отношении шага 3: Мне нравится определение переменных, что они представляют собой комбинации имен значений. Символ - это имя переменной, и ее оценка приведет к ее значению.
Ответ 3
Этот ответ не сильно отличается от других, он просто не предполагает, что вы изначально хотите узнать несколько новых функций и понятий, чтобы понять, что происходит:
-
+
- это символ, расположенный в clojure.core
, который по умолчанию доступен для вашего кода.
- При использовании в вашем коде без каких-либо очень продвинутых намерений, таких как цитирование или поиск его класса, clojure будет искать Вар, на который он указывает.
- Если этот Var является функцией, когда
+
используется в позиции заголовка списка,
clojure попытается вызвать эту функцию (NullPointerException
, если этот Var не указал на функцию). Если в качестве аргумента
другая функция, эта функция может сделать то же самое, чтобы вызвать ее. Это
как работает вызов функции.
дальнейшие комментарии для округления:
Большинство или все языки используют таблицы символов. Будучи несколько динамичным языком, clojure использует этот дополнительный уровень косвенности (функция Symbol → Var →, а не только функция Symbol →), чтобы динамически переписывать, какая функция привязана к тому или иному символу, является более выполнимой и изящной, и это иногда источник любопытства для начинающих.
Поскольку другие ответы несколько чрезмерно подчеркнуты, в противном случае вы можете использовать причудливые вещи, такие как quote it ('+
), чтобы избежать его оценки, или даже проверить его с помощью class
и/или resolve
, как если бы вы заинтересовались проверяя, что это (class
), или какое пространство имён находится (resolve
). Вы также можете вытолкнуть в точке var с помощью var
или #'
. Обычно вы делаете эти причудливые вещи, если пишете макросы или если вы очень экспериментально настроены, особенно при работе в repl; в зависимости от того, в каком стиле вы пишете свои макросы, вы можете на самом деле довольно много цитировать их.
и причудливая иллюстрация для экстра-поискового лица:
Будучи несколько гибким языком, clojure предоставляет api для использования функции Symbol → Var → самостоятельно. Обычно вы никогда не делали этого только для использования функции, потому что, очевидно, это было бы скучно и избыточно, но оно может использоваться здесь для иллюстрации процесса:
(deref (resolve '+))
А именно, символ сначала разрешен к его Var, а затем вещь, на которую указывает Вар. Это просто иллюстрирует двухэтапный процесс для получения функции (функция "Символ → Вар →)), которая происходит за кулисами. Надеюсь, вы избежали чтения этой дополнительной части.
TL; DR
ответ на исходный вопрос просто: Да.