Почему в Clojure есть «ключевые слова» в дополнение к «символам»?

130

У меня есть кое-какие знания о других Lisp (особенно о Scheme) с давних времен. Недавно я читал о Clojure . Я вижу, что в нем есть и «символы», и «ключевые слова». Знакомые символы, но не ключевые слова.

Есть ли у других Лиспов ключевые слова? Чем ключевые слова отличаются от символов, если они не имеют другого обозначения (например, двоеточия)?

Лоуренс Гонсалвес
источник

Ответы:

139

Вот документация Clojure по ключевым словам и символам.

Ключевые слова - это символьные идентификаторы, которые оцениваются сами по себе. Они предоставляют очень быстрые тесты на равенство ...

Символы - это идентификаторы, которые обычно используются для обозначения чего-то еще. Их можно использовать в формах программ для обращения к параметрам функций, привязкам let, именам классов и глобальным переменным ...

Ключевые слова обычно используются как легкие «постоянные строки», например, для ключей хэш-карты или значений диспетчеризации мультиметода. Символы обычно используются для именования переменных и функций, и реже манипулируют ими как объектами напрямую, за исключением макросов и т.п. Но ничто не мешает вам использовать символ везде, где вы используете ключевое слово (если вы не против постоянно их цитировать).

Самый простой способ , чтобы увидеть разницу, чтобы прочитать Keyword.javaи Symbol.javaв источнике Clojure. Есть несколько очевидных различий в реализации. Например, у символа в Clojure могут быть метаданные, а у ключевого слова - нет.

Помимо синтаксиса с одним двоеточием, вы можете использовать двойное двоеточие для создания ключевого слова с указанием пространства имен.

user> :foo
:foo
user> ::foo
:user/foo

В Common Lisp есть ключевые слова, как в Ruby и других языках. Конечно, на этих языках они немного отличаются. Некоторые различия между ключевыми словами Common Lisp и Clojure:

  1. Ключевые слова в Clojure не являются символами.

    user> (symbol? :foo)  
    false
  2. Ключевые слова не принадлежат ни к какому пространству имен, если вы специально не указали их:

    user> (namespace :foo)
    nil
    user> (namespace ::foo)
    "user"

(Спасибо Райнеру Йосвигу за то, что он дал мне идеи, на что можно посмотреть.)

Брайан Карпер
источник
10
Это объясняет, в чем заключаются различия, но не объясняет, почему необходимы две разные конструкции. Разве Clojure не смог создать что-то, объединив возможности Keyword и Symbol?
25
Ключевые слова легкие и имеют удобный синтаксис, я думаю, это все, что нужно. Язык отлично работал бы без них, но они хороши, и они очень широко используются. Вы не можете объединить их способности, потому что ключевые слова всегда самооценки (т.е. вы не можете использовать их в качестве имен переменных или функций), а символы в целом не всегда могут быть самооценками.
Брайан Карпер,
1
Кажется , ключевые слова являются более полезными в качестве ключей в HashMaps и т.д. , поскольку они не меняют один раз оценивали: (eval (eval ':a))против (eval (eval ''a)). Есть ли другие преимущества? По производительности они идентичны?
Кристианлм
5
(идентично?: qwe: qwe) -> верно. (идентично? 'qwe' qwe) -> ложь. Внутри символов используется интернированная строка, поэтому сравнение тоже происходит быстро.
desudesudesu 05
29

В Common Lisp есть ключевые слова.

Ключевые слова - это тоже символы.

(symbolp ':foo) -> T

Что делает ключевые слова особенными:

  • : foo анализируется программой чтения Common Lisp как ключевое слово символа :: foo
  • ключевые слова оценивают сами себя:: foo ->: foo
  • домашний пакет символов ключевого слова - это пакет KEYWORD: keyword: foo ->: foo
  • ключевые слова экспортируются из пакета KEYWORD
  • ключевые слова являются константами, нельзя присвоить другое значение

В противном случае ключевые слова представляют собой обычные символы. Таким образом, ключевые слова могут называть функции или иметь списки свойств.

Помните: в Common Lisp символы принадлежат пакету. Это можно записать так:

  • foo, когда символ доступен в текущем пакете
  • foo: bar, когда символ FOO экспортируется из пакета BAR
  • foo :: bar, когда символ FOO находится в пакете BAR

Для символов ключевого слова это означает, что: foo, keyword: foo и keyword :: foo - это один и тот же символ. Таким образом, два последних обозначения обычно не используются.

Итак: foo просто анализируется как входящий в пакет KEYWORD, предполагая, что указание имени пакета перед именем символа означает по умолчанию пакет KEYWORD.

Райнер Йосвиг
источник
6

Ключевые слова - это символы, которые оценивают сами себя, поэтому вам не нужно помнить их цитировать.

Грег Хьюгилл
источник
5
Это оно? Ввод: вместо 'не кажется большой победой, тем более, что: на большинстве клавиатур: это дополнительное нажатие клавиши.
Лоуренс Гонсалвес,
11
На самом деле, это больше, чем просто персонаж. Ключевые слова остаются ключевыми словами после оценки, тогда как символы оцениваются для того, к чему они привязаны. Это больше похоже на семантическую разницу, потому что они обычно используются для разных целей.
Грег Хьюгилл,
13
Ключевые слова не являются символами в Clojure
Дэвид Пламптон
4

Ключевые слова: также обрабатываются многими коллекциями особым образом, что позволяет использовать действительно удобный синтаксис.

(:user-id (get-users-map))

такой же как

((get-users-map) :user-id)

это делает вещи немного более гибкими

Артур Ульфельдт
источник
21
Это также верно для символов ('a {' a 1 'b 2}) => 1 и ({' a 1 'b 2}' b) => 2.
Йонас,
4

Для ключевых слов хеш-значения вычисляются и кэшируются при первом создании ключевого слова. При поиске ключевого слова как хеш-ключа оно просто возвращает предварительно вычисленное хешированное значение. Для строк и символов хэш пересчитывается при каждом поиске.

Почему ключевые слова с одинаковыми именами всегда идентичны, они содержат свои собственные хеш-значения. Поскольку поиск в картах и ​​наборах осуществляется с помощью хэш-ключей, это способствует повышению эффективности поиска в случае многочисленных поисков, а не в самом поиске.

Иван Пьер
источник
0

Ключевые слова глобальны , символы - нет .

Этот пример написан на JavaScript, но я надеюсь, что он поможет донести мысль.

const foo = Symbol.for(":foo") // this will create a keyword
const foo2 = Symbol.for(":foo") // this will return the same keyword
const foo3 = Symbol(":foo") // this will create a new symbol
foo === foo2 // true
foo2 === foo3 // false

Когда вы создаете символ с помощью Symbolфункции, вы каждый раз получаете отдельный / частный символ. Когда вы запрашиваете символ через Symbol.forфункцию, вы каждый раз будете возвращать тот же символ.

(println :foo) ; Clojure
System.out.println(RT.keyword(null, "foo")) // Java
console.log(System.for(":foo")) // JavaScript

Все они одинаковы.


Имена аргументов функции являются локальными. т.е. не ключевые слова.

(def foo (fn [x] (println x))) ; x is a symbol
(def bar (fn [x] (println x))) ; not the same x (different symbol)
Джон Лейдегрен
источник