Reputation: 3061
I'm trying to extend a simple java class toxi.color.ColorList
with this protocol:
(defprotocol countable
(count [this]))
(extend-protocol countable
ColorList
(count [this]
(.size this)))
when i evaluate this code i see these warning
Warning: protocol #'dat00.protocols/countable is overwriting function count
WARNING: count already refers to: #'clojure.core/count in namespace: dat00.protocols, being replaced by: #'dat00.protocols/count
But this works fine:
(count (ColorList.))
=> 0
But if I try this in same file (or namespace)
(count (range 5))
=> IllegalArgumentException No implementation of method: :count of protocol: #'dat00.protocols/countable found for class: clojure.lang.LazySeq clojure.core/-cache-protocol-fn (core_deftype.clj:541)
So my question is:
Am i misunderstanding some detail about protocols?
Thanks!
Upvotes: 2
Views: 1253
Reputation: 3061
Thanks to all,
I published here 2 working solution I've reached for this case, thanks to all for your comments
In all cases I have had to change the name of the function
It seems that multimethods also (I'm not yet sure about performance consequences...) can solve the expression problem here
(defmulti count type)
(defmethod count toxi.color.ColorList [a]
(.size a))
(defmethod count clojure.lang.LazySeq [a]
(count a))
(defprotocol countable
(get-count [this]))
(extend-protocol countable
ColorList
(get-count [this]
(.size this))
clojure.lang.LazySeq
(get-count [this]
(.count this))
)
(get-count (ColorList.)) => 0
(get-count (range 5)) => 5
that works for me, although what I wanted to do (and with the ns colission I realized my conceptual error) was to use the "same-name-method" of different interfaces in the same ns :) ... suppose I was influenced by clojure/java interop sintaxis (the parameter defines the fn instead of the contrary) –
Upvotes: 0
Reputation: 9266
There is the function clojure.core/count
and the method count
defined in your protocol countable
. Like the warning says: You overwrite the alias named count
to clojure.core/count
by creating an interface which has a method named count
.
The LazySeq
object returned by (range 5)
doesn't implement your countable
protocol. You could still count it with (clojure.core/count (range 5))
.
What you probably want to do is to implement the clojure.lang.Counted
interface instead of your own.
Upvotes: 4
Reputation: 26436
You have a namespace collision.
When you define a protocol, you are defining dispatch functions in the current namespace. If you really want to use "count", you'll have to exclude the clojure.core version in your namespace declaration.
(ns so.protocols
(:refer-clojure :exclude [count]))
Now in that namespace you can define your protocol with a "count" method. If you then want the core version of count in that namespace, you can namespace prefix it clojure.core/count
.
Users of your protocols will then want to alias your namespace. For example,
(ns user
(:require [so.protocols :as p]))
So that p/count
is your protocol method and count
is core.
Upvotes: 4