2

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!

4
  • 1
    Protocol methods cause functions of the same name to be defined in the current namespace. When you call a protocol method from some Clojure code, what you're actually calling is a generated function that looks up and dispatches to the appropriate implementation. Commented Apr 23, 2014 at 19:37
  • Thanks in advance Alex, but how would you solve this case "current namespace conflict"? Commented Apr 23, 2014 at 20:34
  • 1
    Same as you would any other namespace conflict, as described in the answers below. The fact that it happens to be a protocol method causing the conflict is of little importance. Commented Apr 23, 2014 at 21:02
  • Thanks @Alex I've published below the 2 working solutions for this case, with multimethods and extending protocol, both changing the function name Commented Apr 24, 2014 at 11:04

3 Answers 3

4

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.

Sign up to request clarification or add additional context in comments.

1 Comment

thanks for explain the details to solve this namespace overlapping! But my problem (not very well explained) was also related to the expression problem stackoverflow.com/questions/3596366/… , then I wanted to use the same name-method-interfaz in the same case.
4

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.

4 Comments

"What you probably want to do is to implement the clojure.lang.Counted interface instead of your own." - Unfortunately OP wants to extend the protocol to an existing Java class - there's no indication that class can be modified.
Then he should create a Clojure wrapper via defrecord or using reify.
sorry @lgrapenthin i've unchecked your solution because I can not modify the Java class and, defrecord or reify don't work either beacuse there are times that i don't create the instances :(
You have two options: 1. You create a fully featured Clojure wrapper of the ToxicLib (at least the functionality you are using directly or indirectly). It's a lot of work and should only be done if there are significant benefits. 2. This is my advice: Just use Java Interop directly which is a common idiom in Clojure - ColorList is not even immutable, so why would you want to make it fit into Clojure? Just use .size - everything else will confuse readers of your code (I'd think - It's it a Clojure coll?).
0

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) –

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.