Я новичок в clojure и мне это очень нравится. Сейчас я пытаюсь подняться и использовать больше возможностей языка: мультиметоды и протоколы
Я видел много постов в блоге, документов и вопросов, касающихсяна эту тему, но я все еще не уверен, что я понимаю.
Предположим, у меня есть функция, которая меняет поведение в зависимости от типа входного аргумента. Я могу реализовать функцию либо через multimethod или протокол :
;; Multi
(defmulti foo class)
(defmethod foo java.lang.Double [x]
"A double (via multimethod)")
(defmethod do-a-thing java.lang.Long [x]
"A long (via multimethod)")
;; Protocol
(defprotocol Bar
(bar [x] "..."))
(extend-protocol Bar
java.lang.Double
(bar [x] "A double (via protocol)")
java.lang.Long
(bar [x] "A long (via protocol)"))
Обе работы, и кажется, что подход протокола предпочтительнее для целей скорости.
Однако я хотел бы реализовать математическую функцию (например, площадь), которая зависит от входного параметра. Один из подходов заключается в использовании case , но я не нахожу его чистым.
Поэтому я попробовал версию multimethod . Это работает как чудо:
(defmulti area (fn [shape & _]
shape))
(defmethod area :square
[_ x]
(* x x))
(defmethod area :circle
[_ r]
(* r r Math/PI))
(defmethod area :triangle
[_ b h]
(* 1/2 b h))
Но следующая реализация протокола не работает:
(defprotocol Surface
(surface [x] 0.0))
(extend-protocol Surface
:square
(surface [x] (* x x))
:circle
(surface [r] (* r R Math/PI))
:triangle
(surface [b h] (* 1/2 b h)))
Я получаю следующую ошибку:
Execution error (ClassCastException) at user/eval2081 (REPL:1).
class clojure.lang.Keyword cannot be cast to class java.lang.Class (clojure.lang.Keyword is in unnamed module of loader 'bootstrap'; java.lang.Class is in module java.base of loader 'bootstrap')
Myвопросы:
Есть ли способ реализовать эту функцию с протоколами или это проблема multimethod only?
Поддерживают ли протоколы только распознавание на основе type ? Как указано здесь https://stackoverflow.com/a/8074581/1537744?