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

0 votes
in Protocols by

It seems like a metadata protocol implementation overrides extend calls, but not direct implementations. Is this expected? How does choosing a metadata protocol interact with the caching of defprotocol methods? Are there any other gotchas?

Clojure 1.10 .3
user=> (defprotocol A :extend-via-metadata true (foo [this]))
user=> (defrecord Direct [] A (foo [_] :direct))
user=> (foo (->Direct))
user=> (foo (with-meta (->Direct) {`foo (fn [_] :meta)}))
user=> (defrecord Extend [])
user=> (extend-protocol A Extend (foo [_] :extend))
user=> (foo (->Extend))
user=> (foo (with-meta (->Extend) {`foo (fn [_] :meta)}))

1 Answer

+1 vote
selected by
Best answer

Yes, this is expected.

This doesn't really impact the caching of methods, just the selection of the method choice to cache.

edited by
There seems to be one strange corner case that isn't (explicitly) covered by the docs: a metadata impl does not win if the protocol is directly implemented but the direct impl is not provided.

Clojure 1.10.3
user=> (defrecord MissingDirect [] A)
user=> (foo (with-meta (->MissingDirect) {`foo (fn [_] :meta)}))
Execution error (AbstractMethodError) at user/eval208 (REPL:1).
Receiver class user.MissingDirect does not define or inherit an implementation of the resolved method 'abstract java.lang.Object foo()' of interface user.A.

OTOH, if a metadata impl is missing, it defaults to the extends impl. The current wording of the docs doesn't seem to make this discrepancy obvious (but clearly this is all to preserve the fast path).