Как можно написать defprotocol
(и defrecord
для его реализации), объявляющий метод с тем же именем, как и существующей функции, и отправка динамически метод протокола/пластинки iff Я вызываю его с экземпляром протокола/записи, но в противном случае отправляю существующую функцию?Создание defprotocol играть хорошо Clojure (в полиморфно) с существующими функциями
Например, я хочу создать помощник геометрии, которая поддерживает основные арифметические операции (только умножение в этом примере, чтобы держать его в коротком):
(defprotocol SizeOps
(* [this factor] "Multiply each dimension by factor and return a new Size"))
На данный момент я уже получаю некоторые предчувствия теряемого от компилятор:
Внимание: Протокол № 'пользователь/SizeOps является функцией перезаписи *
ВНИМАНИЕ: * уже относится к: #' clojure.core/* в пространстве имен: пользователь, заменяется на: # 'пользователь/*
Тогда реализация:
(defrecord Size [width height]
SizeOps
(* [this factor] (Size. (* width factor) (* height factor))))
Это компилируется нормально, но когда я пытаюсь использовать его, только *
он знает, это один в моем протоколе:
(* (Size. 1 2) 10)
IllegalArgumentException Нет реализации метода:: * протокола: # 'user/SizeOps найдено для класса: java.lang.Long
я могу взломать вокруг этого, полностью определяя функцию ядра *
в моей реализации:
(defrecord Size [width height]
SizeOps
(* [this factor] (Size. (clojure.core/* width factor) (clojure.core/* height factor))))
(* (Size. 1 2) 10)
# user.Size {: ширина 10,: высота 20}
Но я получаю тот же IllegalArgumentException
если попытаюсь позвонить (* 3 4)
позже. Я могу использовать имена clojure.core/*
в моей версии defrecord
, но я хочу, чтобы мои пользователи имели возможность звонить *
на мои Size
записи, а также на Long
, Double
и т. Д., Как обычно.
Похожие Q & A:
- 5438379: расширение
String
с*
оператором, который работает как Python:(* "!" 3)
⇒"!!!"
, но затеняет сердечника*
так же, как в моем примере - 6492458: за исключением основных функций, таких как
(ns user (:refer-clojure :exclude [*]))
избегает предупреждения о перезаписи, но также позволяет избежать этой функции :( - 1535235: то же самое, с жест в сторону использования Многометодная, но без деталей
Я подозреваю, что правильное решение лежит где-то в функциональности отправки более низкого уровня, как defmulti
и defmethod
или deftype
/derive
, но я не очень знаком с нюансами Clojure's runtime polymorphism. И я буду иметь целый ряд Size
, Point
, Rectangle
, Circle
и т.д., типов, каждый из которых поддерживает некоторое подмножество +
, -
, *
, /
операции, так что я хотел бы знать, если есть способ сказать defprotocol
, чтобы участвовать/строить на полиморфизме любых существующих функций, а не просто перезаписывать их.