Ключевые слова и символы

Разбираемся в двух похожих, но разных сущностях Clojure: символах и ключевых словах.

Символ — имя, которое что-то обозначает (функцию, переменную). Ключевое слово — самоозначающая константа, которая обозначает саму себя.

Символы: имена для вещей

Символ — это просто имя, например x, + или println. Когда компилятор видит символ в позиции для вычисления, он ищет, на что тот указывает (значение переменной или функцию). Символы — то, чем вы называете определения через def и defn.

(def цена 100)
; цена - символ, указывающий на число 100
цена  ; => 100

Ключевые слова: константы, означающие себя

Ключевое слово начинается с двоеточия: :name, :age, :status. В отличие от символа, оно не ищет никакого значения — оно есть своё значение. :age всегда равно :age, как число 5 всегда равно 5.

:status        ; => :status
(= :a :a)      ; => true
(keyword "x")  ; => :x  (строку можно превратить в keyword)

Вывод:

:status
true
:x

Зачем нужны ключевые слова

Главное применение — ключи в отображениях (map). Ключевые слова идеально подходят на роль имён полей: они быстрые, читаемые и сами умеют доставать значение из map.

(def пользователь {:имя "Аня" :возраст 30})

; keyword можно вызвать КАК ФУНКЦИЮ от map:
(:имя пользователь)     ; => "Аня"
(:возраст пользователь) ; => 30

; И наоборот, map работает как функция от ключа:
(пользователь :имя)     ; => "Аня"

Вывод:

"Аня"
30
"Аня"

Как работает под капотом

Ключевые слова интернируются: одинаковые :age в разных местах программы — это один и тот же объект в памяти. Поэтому сравнение двух keyword мгновенно (сравниваются ссылки, а не символы строки). Это делает их идеальными ключами в хэш-отображениях. Символы же при вычислении проходят разрешение (resolution) — поиск того, на что они указывают в текущем пространстве имён.

Символ против ключевого слова: памятка

СимволКлючевое слово
Записьname:name
Что значитуказывает на что-тоозначает само себя
При вычисленииищет значениевозвращает себя
Типичная рольимена def/defn, функцииключи в map, метки

Частые ошибки

  • Путать :name и name. Первое — константа-метка, второе — имя, которое будет вычислено.
  • Использовать строки вместо keyword в map. Строки работают, но keyword быстрее, короче и удобнее как функции.
  • Ставить двоеточие в конце. Ключевое слово начинается с двоеточия: :age, а не age:.

Итоги

  • Символ — имя, указывающее на значение; при вычислении разрешается.
  • Ключевое слово (с двоеточия) означает само себя и интернируется.
  • Keyword — стандартные ключи в map; их можно вызывать как функцию от map.
  • Интернирование делает сравнение и поиск по keyword очень быстрыми.
Проверьте себя
1. Как в Clojure записывается ключевое слово (keyword)?
Aname
B"name"
C:name
D'name
2. Что вернёт выражение (:имя {:имя "Аня"})?
AОшибку
B"Аня"
C:имя
Dnil
3. Чем символ отличается от ключевого слова при вычислении?
AСимвол разрешается (ищет значение), keyword возвращает себя
BОни одинаковы
CKeyword разрешается, символ возвращает себя
DСимвол — это всегда число