tangrammer
tangrammer

Reputation: 3061

how to extend java class with protocol having a method the same name than a clojure.core function?

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

Answers (3)

tangrammer
tangrammer

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

Leon Grapenthin
Leon Grapenthin

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

A. Webb
A. Webb

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

Related Questions