Share your thoughts in the 2021 Clojure Community Survey!

Welcome! Please see the About page for a little more info on how this works.

0 votes
in Protocols by

The forms required for implementing arity-overloaded protocol methods are inconsistent between the "extend-**" macros and "defrecord".

The "extend" family of macros requires overloaded method definitions to follow the form used by defn:

(method ([arg1] ...) ([arg1 arg2] ...))

However, "defrecord" requires implementations to be defined separately:

(method [arg1] ...) (method [arg1 arg2] ...)

Furthermore, the error modes if you get it wrong are unhelpful.

If you use the "defrecord" form with "extend-**", it evals successfully, but later definitions silently overwrite lexically previous definitions.

If you use the "extend-**" form with "defrecord", it gives a cryptic error about "unsupported binding form" on the body of the method.

This is not the same issue as CLJ-1056: That pertains to the syntax for declaring a protocol, this problem is with the syntax for implementing a protocol.

`

(defprotocol MyProtocol
(mymethod

[this arg]
[this arg optional-arg]))

(extend-protocol MyProtocol
Object
(mymethod

([this arg] :one-arg)
([this arg optional-arg] :two-args)))

;; BAD! Blows up with "Unsupported binding form: :one-arg"
(defrecord MyRecord []
MyProtocol
(mymethod

([this arg] :one-arg)
([this arg optional-arg] :two-args)))

;; Works...
(defrecord MyRecord []
MyProtocol
(mymethod [this arg] :one-arg)
(mymethod [this arg optional-arg] :two-args))

;; Evals...
(extend-protocol MyProtocol
Object
(mymethod [this arg] :one-arg)
(mymethod [this arg optional-arg] :two-args))

;; But then... Error! "Wrong number of args"
(mymethod :obj :arg)

;; 2-arg version is invokable...
(mymethod :obj :arg1 :arg2)
`

4 Answers

0 votes
by
_Comment made by: pparkkin_

Attached a patch for this.

For defrecord, I check which style is used for defining methods, and transform into the original style if the new style is used. For the check I do what I believe defn does, which is {{(vector? (first fdecl))}}.

For extend-*, I skip the checking, and just transform everything into the same format.

Tests included for both.

All tests pass.
0 votes
by

Comment made by: richhickey

What the proposal?

0 votes
by
_Comment made by: tsachev_

This hit me too.

I workaround this using {{extend}} for the protocols that have multi arity methods
i.e. for the above example

(extend Object
  MyProtocol
  {:mymethod (fn ([^Object this arg] :one-arg)
                 ([^Object this arg optional-arg] :two-args))})
0 votes
by
Reference: https://clojure.atlassian.net/browse/CLJ-825 (reported by alex+import)
...