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

Разница между символами и Vars в Clojure

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

Итак, что происходит, шаг за шагом, когда я просто вводил "+" в REPL?

  • Символ получает квалификацию в пространстве имен, в этом случае Clojure.core
  • Затем в некоторой таблице символов есть информация, которая + ссылается на var
  • Когда этот var оценивается, результатом является значение функции?
4b9b3361

Ответ 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

ответ на исходный вопрос просто: Да.