Плавкие предохранители типа / fn объединяются в полиморфный fn через «анонимный» протокол и метод - PullRequest
0 голосов
/ 24 апреля 2018

Моя цель - функция / макрос, который работает следующим образом:

(def f (polymorphic-fn
          java.lang.Long   (fn [a] (inc a))
          java.lang.String (fn [a] (str a "x"))))
(f 1)     ;; => 2
(f "abc") ;; => "abcx"

Так как диспетчеризация протоколов на основе типов имеет лучшую производительность, я думал создать «анонимный» протокол для «слитой» функции с помощью макроса, подобного следующему:

(defmacro polymorphic-fn
  [& pairs]
  (let [proto  (gensym)
        method (gensym)
        extends (for [[type handler] pairs]
                  `(extend ~type ~proto {(keyword ~method) ~handler}))]
    `(do
       (defprotocol ~proto (~method [e#]))
       ~@extends
       ~method)))

Это приводит к ошибке: Unable to resolve symbol: G__18707.

Есть ли способ вернуть «анонимный» метод или есть лучший способ реализовать такую ​​функцию?

Ответы [ 2 ]

0 голосов
/ 25 апреля 2018

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

Многие другие def... сгенерируют макрос "вызов", который будет интернировать символ во время раскрытия макроса (итаким образом, компилятор останется доволен).

Чтобы исправить это, вы можете просто объявить его заранее.Это работает, так как declare является макросом, будет расширен и компилятор будет счастлив:

(defmacro polymorphic-fn
  [& pairs]
  (let [proto (gensym "proto")
        method (gensym "prot-method-")
        extends (for [[type handler] (partition 2 pairs)]
                  `(extend ~type ~proto {~(keyword (str method)) ~handler}))]
    `(do
       (declare ~method)
       (defprotocol ~proto (~method [e#]))
       ~@extends
       ~method)))

Примечание: я также исправил вызов keyword в этом.

0 голосов
/ 24 апреля 2018

Я думаю, вы просто хотите использовать обычные протоколы вместе с extend-type:

(defprotocol Fooable
  (foo [this]) )
(extend-type java.lang.Long
  Fooable
  (foo [some-long] (inc some-long)))
(extend-type java.lang.String
  Fooable
  (foo [any-string] (str any-string "-and-more")))

с результатом:

(foo 3)        => 4
(foo "hello")  => "hello-and-more"

Может быть возможно использовать макрос, чтобы скрыть имя протокола, используя автогенсим, но я не вижу в этом смысла. Просто игнорируйте имя протокола 'Fooable', и вы получите тот же результат.

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

Вы также можете имитировать функциональность протокола, используя cond:

(defn bar [it]
  (cond
    (= (class it) java.lang.Long) (inc it)
    (= (class it) java.lang.String) (str it "-and-more")))

(bar 7)            => 8
(bar "farewell")   => "farewell-and-more"

Вы можете определить функцию для генерации bar, как вы делаете с polymorphic-fn, если хотите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...