Почему использование ключевых слов или символов в качестве функций для поиска значений с карт работает?
Цитата из Радости Clojure, раздел 4.3.1 -
Поскольку ключевые слова являются самооценками и обеспечивают быстрые проверки равенства, они почти всегда используются в контексте ключей карты. Не менее важной причиной использования ключевых слов в качестве ключей карты является то, что они могут использоваться как функции, принимая карту в качестве аргумента для выполнения поиска значений:
(def population {:zombies 2700, :humans 9})
(:zombies population)
;=> 2700
(println (/ (:zombies population)
(:humans population))
"zombies per capita")
; 300 zombies per capita
Мне не кажется, что здесь происходит. Как-то (:zombies population)
нужно преобразовать в (get population :zombies)
, правильно? Как именно это работает? Ключевое слово оценивает себя, а не функцию. Читает ли читатель случаи, когда первое, что входит в список, является ключевым словом и добавляет get и перемещает ключевое слово в конец списка?
Ответы
Ответ 1
Ссылка официальная документация:
Ключевые слова реализуют IFn для invoke() одного аргумента (карты) с необязательным вторым аргументом (значение по умолчанию). Например (: mykey my-hash-map: none) означает то же самое, что (get my-hash-map: mykey: none). См. Get.
И Clojure может вызывать ключевое слово как функцию, потому что он реализует тот же интерфейс, что и функция. То же самое для символов...
Ответ 2
Ключевые слова являются функциями во всех отношениях. Магия читателя не задействована, поскольку вы увидите, если вы попробуете (let [m {:humans 100}, k :humans] (k m))
. Надеюсь, вы согласитесь с тем, что читатель не может превратить это в get (возможно, компилятор, но вы можете притвориться, что значение t21 зависит от выражения if, которое компилятор не может предсказать, например, пользовательский ввод).
Поскольку типы данных типа Clojure являются интерфейсами, а объекты Java могут реализовывать множество интерфейсов, часть данных может иметь несколько типов. Это ключевое слово? Да. Это функция? Также да:
user> (keyword? :k)
true
user> (ifn? :k)
true
user> (.invoke :k {:k 1})
1
Ответ 3
Ключевые слова реализуют IFn,
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Keyword.java
и его метод invoke обрабатывает вызов get.