Как работает <имя_пользователя> defrecord method name resolution?

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

 user=> (defprotocol Eat (eat [this]))
 Eat
 user=> (defrecord animal [name] Eat (eat [this] "eating"))
 user.animal
 user=> (eat (animal. "bob"))
 "eating"
 user=> (.eat (animal. "bob"))
 "eating"
 user=> 

Под поверхностью, что там происходит? Существуют ли новые функции clojure? Что происходит, когда есть определенные вами функции, которые имеют одно и то же имя (возможно ли это?), Как разрешаются эти двусмысленности?

Кроме того, возможно ли "импортировать" java-методы для других объектов Java, чтобы вам не понадобилось. оператора, чтобы поведение было как указано выше? (Например, для объединения пользовательского интерфейса)

Ответы

Ответ 1

Когда вы определяете протокол, каждый из его методов создается как функции в ваших текущих пространствах имен. Из этого следует, что у вас не может быть двух протоколов, определяющих одну и ту же функцию в одном и том же пространстве имен. Это также означает, что вы можете иметь их в разных пространствах имен и что данный тип может расширять оба [1] из них без каких-либо nameclash, потому что они помещаются в имена (в отличие от Java, где один класс не может реализовать два интерфейса с омонимическими методами).

С точки зрения пользователя методы протокола ничем не отличаются от простых старых неполиморфных функций.

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

Следовательно, вы не можете использовать форму взаимодействия на объекте, для которого расширение не было предоставлено встроенным:

(defrecord VacuumCleaner [brand model]
(extend-protocol Eat
  VacuumCleaner
  (eat [this] "eating legos and socks"))

(.eat (VaacumCleaner. "Dyson" "DC-20"))
; method not found exception

У компилятора есть специальная поддержка функций протокола, поэтому они компилируются как проверка экземпляра, за которым следует вызов виртуального метода, поэтому, когда применимо (eat ...) будет работать так же быстро, как (.eat ...).

Чтобы ответить на "можно импортировать java-методы", вы можете обернуть их в регулярные fns:

(def callme #(.callme %1 %2 %3))

(очевидно, вам может понадобиться добавить другие атрибуты для учета перегрузок и ввести подсказки для удаления отражения)

[1], однако вы не можете расширять как встроенные (хотя бы один из них должен быть в форме extend-*) из-за ограничения реализации