Bug in extend-protocol macro? (Clojure 1.3.0)

198 views
Skip to first unread message

Peter Taoussanis

unread,
Dec 27, 2011, 1:21:12 AM12/27/11
to Clojure
Hi there,

I'm using Clojure 1.3.0 and am running into what seems like an edge-
case problem with extend-protocol (?)...

Given (defprotocol MyProtocol (action [x])),

(extend-protocol MyProtocol
(Class/forName "[B") (action [x] "ByteArray")
java.lang.Integer (action [x] "Integer"))

expands to (do (clojure.core/extend-type java.lang.Integer MyProtocol
(action [x] "Integer")) (clojure.core/extend-type (Class/forName "[B")
MyProtocol (action [x] "ByteArray")))

which works as expected. Note the acrobatics to get a byte[] class.


BUT, if I reorder the implementations,

(extend-protocol MyProtocol
java.lang.Integer (action [x] "Integer")
(Class/forName "[B") (action [x] "ByteArray"))

the expansion becomes (do (clojure.core/extend-type java.lang.Integer
MyProtocol (action [x] "Integer") (Class/forName "[B") (action [x]
"ByteArray"))), which is malformed.


Am I correct in assuming this isn't the desired behaviour? Thanks!

--
Peter Taoussanis

Alan Malloy

unread,
Dec 27, 2011, 1:35:28 AM12/27/11
to Clojure
extend-protocol groups things by splitting them into seqs and symbols.
(Class/forName "[B") is a seq, so it's an implementation of a protocol
function, not a class. If you want to do these sorts of gymnastics you
can use the underlying extend primitive directly - I believe it just
takes a seq of protocol/impl-map pairs, so you get/have to do the
grouping yourself.

Of course, you could use extend-type instead of extend, but if you
need some expression to figure out which protocol to extend you're in
the same boat AFAIK, which is why I might recommend using extend.

Peter Taoussanis

unread,
Dec 28, 2011, 2:34:07 AM12/28/11
to Clojure
Hi Alan,

Thanks- that explains it: dropping to extend works as expected.

--
Peter Taoussanis

Marshall T. Vandegrift

unread,
Dec 30, 2011, 2:34:28 PM12/30/11
to clo...@googlegroups.com
Peter Taoussanis <ptaou...@gmail.com> writes:

> Thanks- that explains it: dropping to extend works as expected.

Another option I've been making use of for exactly this situation is to
use the #= reader macro to evaluate the Class/forName at read-time.
Something like:

(extend-protocol MyProtocol
java.lang.Integer (action [x] "Integer")

#=(Class/forName "[B") (action [x] "ByteArray"))

It may not be ideal stylistically, but lets you use extend-protocol in
such situations.

-Marshall

Meikel Brandmeyer

unread,
Dec 30, 2011, 3:20:46 PM12/30/11
to clo...@googlegroups.com
Hi,

Am 30.12.2011 um 20:34 schrieb Marshall T. Vandegrift:

> Another option I've been making use of for exactly this situation is to
> use the #= reader macro to evaluate the Class/forName at read-time.
> Something like:
>
> (extend-protocol MyProtocol
> java.lang.Integer (action [x] "Integer")
> #=(Class/forName "[B") (action [x] "ByteArray"))
>
> It may not be ideal stylistically, but lets you use extend-protocol in
> such situations.

The “right” thing is probably allowing strings as well in extend-protocol. It is allowed in several other places like gen-class or type hinting. So it seems consistent to allow also (extend-protocol "[B" …).

Sincerely
Meikel

Alan Malloy

unread,
Dec 30, 2011, 4:13:04 PM12/30/11
to Clojure
On Dec 30, 11:34 am, "Marshall T. Vandegrift" <llas...@gmail.com>
wrote:
Stuff like this always worries me. It may happen to work now, but I
doubt if that's a guarantee. extend-protocol's contract is that you
give it a Symbol, which it resolves into a Class. It surely isn't
expecting a Class as an argument, because you can't enter that as a
source-code literal. If you give it a Class, it could conceivably do
something dreadful like try to call (resolve s) on it to figure out
what Class you mean, and that will fail when passed a Class.

For that matter, trying out your example, it doesn't seem to work for
me. The first fix is to use java.lang.Class instead of just Class,
since reader evaluation happens in a no-frills environment. But after
that, it seems the extend-protocol just silently does nothing:

repl-1=> (defprotocol P)
P
repl-1=> (extend-protocol P #=(java.lang.Class/forName "[B"))
nil
repl-1=> (satisfies? P (Class/forName "[B"))
false

Dave Ray

unread,
Dec 30, 2011, 4:53:19 PM12/30/11
to clo...@googlegroups.com

satisfies? works on an instance:

user=> (satisfies? P (byte-array []))
true


Dave

Marshall T. Vandegrift

unread,
Dec 31, 2011, 12:01:41 PM12/31/11
to clo...@googlegroups.com
Alan Malloy <al...@malloys.org> writes:

> Stuff like this always worries me. It may happen to work now, but I
> doubt if that's a guarantee. extend-protocol's contract is that you
> give it a Symbol, which it resolves into a Class. It surely isn't
> expecting a Class as an argument, because you can't enter that as a
> source-code literal. If you give it a Class, it could conceivably do
> something dreadful like try to call (resolve s) on it to figure out
> what Class you mean, and that will fail when passed a Class.

I'm not entirely happy with it either, but I don't think the situation
is quite that dire. The docstring for `extend-protocol' doesn't say
anything about *how* it divides up the stanzas for extended types, and
as pointed out elsewhere in this thread the implementation uses seq vs
not-seq, not symbol? vs not-symbol?. The docstrings for `extend-type'
and `extend' explicitly say that they take a "type/class" -- I'd
interpret that to mean that they can be called with anything which
resolves to a type/class via normal resolution methods, including a
literal value generated via #=.

> For that matter, trying out your example, it doesn't seem to work for
> me. The first fix is to use java.lang.Class instead of just Class,
> since reader evaluation happens in a no-frills environment.

Apologies -- I just modified the OP's original example without thinking
it all the way through or running the code. One does need to specify
java.lang.Class/forName, which is what I've been doing in my own source.

> But after that, it seems the extend-protocol just silently does
> nothing:

(Addressed by another poster.)

-Marshall

Reply all
Reply to author
Forward
0 new messages