espeed
espeed

Reputation: 4824

When implementing a Clojure protocol, can an overloaded method call its overloaded counterpart?

Here's part of an overloaded protocol definition:

(defprotocol ClientProtocol
  (create-edge
    [this outV label inV] 
    [this outV label inV data])

And here's part of its implementation:

(ns bulbs.neo4jserver.client
  (:use [bulbs.base.client :only [ClientProtocol]]))

(deftype Neo4jClient [ns config]
  ClientProtocol

  (create-edge
    [this outV label inV data]
    (let [inV-uri (utils/normalize-uri (build-path neo4j-uri vertex-path inV))
          path (build-path vertex-path, outV, "relationships")
          params {:to inV-uri, :type label, :data (first data)}]
      (post config path params)))

  (create-edge
    [this outV label inV]
    ;; it doesn't like this call...
    (create-edge this outV label inV nil)) 

It's spewing this error when the second method tries to call the first:

java.lang.RuntimeException: Unable to resolve symbol: create-edge in this context

I had both definitions working earlier in SLIME when I compiled it with the first function and then went back and added the second.

But when I moved the protocol definition into a separate file and tried to re-compile the whole thing, it throws an error when the second method tries to call the first, presumably because the first method hasn't been defined yet.

The Clojure reify docs have this comment:

If a method is overloaded in a protocol/interface, multiple independent method definitions must be supplied.

http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/reify

I suppose these are not independent definitions.

What's the right way to approach this?

Upvotes: 3

Views: 2025

Answers (3)

kotarak
kotarak

Reputation: 17299

Your namespace declaration is wrong.

(ns bulbs.neo4jserver.client
  (:use [bulbs.base.client :only [ClientProtocol]]))

Protocol functions are normal Clojure functions and have to treated as such. So you have to include them in your :only clause.

(ns bulbs.neo4jserver.client
  (:use [bulbs.base.client :only [create-edge ClientProtocol]]))

Upvotes: 3

Charles Duffy
Charles Duffy

Reputation: 295500

What you're doing here works fine.

Consider this example:

(defprotocol ClientProtocol
  (create-edge
    [this outV label inV]
    [this outV label inV data]))

(deftype SampleClient []
  ClientProtocol

  (create-edge
    [this outV label inV]
      (create-edge this outV label inV {}))

  (create-edge
    [this outV label inV data]
      {:action :create-edge :this this :outV outV :label label :inV inV :data data}))

Behavior when run is as-expected:

; => (create-edge (SampleClient.) 1 2 3)
{:action :create-edge, :this #<SampleClient user.SampleClient@63a2cc03>,
 :outV 1, :label 2, :inV 3, :data {}}
; => (create-edge (SampleClient.) 1 2 3 4)
{:action :create-edge, :this #<SampleClient user.SampleClient@7144c1a4>,
 :outV 1, :label 2, :inV 3, :data 4}

Upvotes: 0

Arthur Ulfeldt
Arthur Ulfeldt

Reputation: 91554

Here is a stipped down example that works in clojure 1.4.0

(ns hello.core)
(defprotocol ClientProtocol
 (create-edge
    [this outV label inV] 
    [this outV label inV data]))

(deftype Neo4jClient []
  ClientProtocol

 (create-edge
   [this outV label inV data]
    4)

  (create-edge
    [this outV label inV]
    (create-edge this outV label inV nil)))

hello.core> (create-edge (Neo4jClient.) 2 3 4)
4

hello.core> (create-edge (Neo4jClient.) 2 3 4 5)
4

and here is a mutually recursive example (no base case)

(deftype Neo4jClient []
  ClientProtocol

  (create-edge
    [this outV label inV data]
    (println "OMG im in a looooooop...")
    (create-edge this 1 2 3))

  (create-edge
    [this outV label inV]
    (println "AHHH im in a looooooop...")
    (create-edge this 1 2 3 4)))


hello.core> (create-edge (Neo4jClient.) 1 2 3 4)
OMG im in a looooooop...
AHHH im in a looooooop...
OMG im in a looooooop...
AHHH im in a looooooop...
OMG im in a looooooop...
AHHH im in a looooooop...
OMG im in a looooooop...
AHHH im in a looooooop...

Upvotes: 2

Related Questions