Смешивание definterface и defprotocol

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

Наличие:

(defprotocol ExtendsExpression
  (toTree [this]))

(extend-type String
  ExtendsExpression
  (toTree [this] (symbol this)))

(extend-type Number
  ExtendsExpression
  (toTree [this] this))

(definterface Expression
  (toTree []))

(defrecord Expression1 [^String oper arg]
  Expression
  (toTree [this]
    (list (symbol oper) (toTree arg))))


(defrecord Expression2 [^String oper arg1 arg2]
  Expression
  (toTree [this]
    (list (symbol oper) (toTree arg1) (toTree arg2))))

(defrecord Expression3 [^String oper arg1 arg2 arg3]
  Expression
  (toTree [this]
    (list (symbol oper) (toTree arg1) (toTree arg2) (toTree arg3))))

Я пытаюсь использовать его как:

(toTree (Expression3. "+" "a" "b" (Expression2. "*" "c" "d")))

но я получаю:

IllegalArgumentException No implementation of method: :toTree of protocol: #'user/ExtendsExpression found for class: user.Expression3  clojure.core/-cache-protocol-fn (core_deftype.clj:541)

Почему clojure пытается вызвать toTree of ExtendsExpression для Expression3? Я ожидаю, что для Expression3 он вызовет метод toTree интерфейса Expression.


person Lambder    schedule 16.08.2013    source источник


Ответы (1)


Хорошо понял ;)

(defprotocol ExtendsExpression
  (to-tree [this]))

(extend-type String
  ExtendsExpression
  (to-tree [this] (symbol this)))

(extend-type Number
  ExtendsExpression
  (to-tree [this] this))

(definterface Expression
  (toTree []))

(defrecord Expression1 [^String oper arg]
  ExtendsExpression
  (to-tree [this]
    (list (symbol oper) (to-tree arg)))
  Expression
  (toTree [this] (to-tree this)))


(defrecord Expression2 [^String oper arg1 arg2]
  ExtendsExpression
  (to-tree [this]
    (list (symbol oper) (to-tree arg1) (to-tree arg2)))
  Expression
  (toTree [this] (to-tree this)))

(defrecord Expression3 [^String oper arg1 arg2 arg3]
  ExtendsExpression
  (to-tree [this]
    (list (symbol oper) (to-tree arg1) (to-tree arg2) (to-tree arg3)))
  Expression
  (toTree [this] (to-tree this)))

(to-tree (Expression3. "+" "a" "b" (Expression2. "*" "c" "d"))) ;=> (+ a b (* c d))

и записи реализуют интерфейс Expression, поэтому я могу легко вызывать их из java:

(.toTree (Expression3. "+" "a" "b" (Expression2. "*" "c" "d"))) ;=> (+ a b (* c d))

просто чтобы проверить, какие интерфейсы реализует Expression3:

 (-> Expression3 clojure.reflect/reflect :bases pprint)
#{clojure.lang.IHashEq java.io.Serializable clojure.lang.IKeywordLookup
  clojure.lang.IPersistentMap clojure.lang.IRecord java.lang.Object
  user.ExtendsExpression clojure.lang.IObj clojure.lang.ILookup
  user.Expression java.util.Map}
person Lambder    schedule 16.08.2013