Hi Juan,
I have to admit you're a life saver! You didn't say anything that I did
not know but you did make a couple of observations that made me have a
closer look on my code...For example you noticed that the protocol
extension to String was never registered...That is the *real* problem
here....I was trying to register two 'getDistance' with 2 and 3 args
respectively while the protocol only defines arities 3 and 4...I don't
really need to add an arity of 2 on the protocol - what I need to do is
properly extend the protocol to String so that both arities are
registered...then, it's pretty obvious that the record should be like
this (see below - notice the ignored 2nd arg when delegating) and
everything works as expected! :)
(defrecord LevenshteinDistance []
IDistance
(getDistance [_ s1 s2]
(getDistance s1 _ s2)) ;;delegate to string for this
(getDistance [_ s1 s2 weight]
(getDistance s1 _ s2 weight))) ;;delegate to string for this
The fact of the matter is that Clojure code can get so incredibly terse
that after a while my eyes hurt! That said, i certainly prefer my eyes
hurting than my brain hurting... ;)
thanks again for taking the time to poke around...as I said, your
observations were spot on!
cheers,
Jim
On 15/02/13 06:38, juan.facorro wrote:
Hi Jim:
I think the problem is that you are actually calling the *getDistance
*protocol function with only 2 arguments in the line in bold below:
(defrecord LevenshteinDistance []
IDistance
(getDistance [_ s1 s2]
*(getDistance s1 s2)) ;; <- Calling a getDistance function with 2 args*
(getDistance [_ s1 s2 weight]
(getDistance s1 s2 weight)))
While in the protocol definition there's only a 3 and 4 arguments
declarations for getDistance.
(defprotocol IDistance
(getDistance
[this s1 s2] ; 3 args
[this s1 s2 m-weight])) ; 4 args
You could add a 2 arguments override for the *getDistance* protocol
function and it would work by calling the 2 args implementation added
to String, which is actually never registered in the protocol and goes
unnoticed for the reason that follows, which I myself found out while
experimenting with your code, you can skip it if you like, I just had
a little fun investigating some Clojure code :).
When using *extend-type*, any implementation for a protocol function
with a number of args not present in the protocol's declaration
doesn't seem to produce any errors or warnings (*extend* presents the
same behavior, which makes sense since *extend-type* uses it).
(defprotocol SomeProtocol
(some-function [this x] [this x y]))
(defrecord SomeRecord [])
(extend-type SomeRecord
SomeProtocol
(some-function
([_] (println "1-arg")) ; this is not declared
([_ _ _ _ _] (println "5-arg")) ; this is not declared
([_ x y]
(some-function x y)))
However, a*CompilerException* is thrown when implementing a protocol
using the *defrecord*macro, and declaring a non-existing override for
the function. I looked a little bit into the code of *defrecord *and
the reason for this seems to be that it ultimately uses *deftype*
which actually creates a class that implements the methods for the
Java interface that the protocol defines, the compiler checks in this
case if there's any method with the name and arity with the supplied
implementation, and throws an exception if it doesn't.
(defprotocol SomeProtocol
(some-function [this x] [this x y]))
(defrecord SomeRecord []
SomeProtocol
(some-function [_] (println "1-arg")) ; this is not declared
(some-function [_ x y]
(some-function x y)))
#<CompilerException java.lang.IllegalArgumentException: Can't define
method not in interfaces: some_function, compiling: (file.clj)>
This seems to indicate that using extend (or extend-type) vs. deftpye
(or defrecord) for implementing a protocol yields two different
results, the former registers the function implementations in the
protocol using the map supplied and the latter actually creates a
class method for the record or type class generated.
This was not obvious at all to me and I think I even recall reading
somewhere (can't remember exactly where and can't find it right now)
that the defrecord "inline" implementation was just a convenience form
for extend/extend-type, but is it possible that it's actually more
performant to use the defrecord/deftpye?
Hope it helps,
Juan
On Thursday, February 14, 2013 5:16:53 PM UTC-3, Jim foo.bar wrote:
let me explain with an example:
;;in some namespace x
(defprotocol IStemmable
(stem [this token] [this token lang])
(getRoot [this token dictionary]))
(defprotocol IDistance
(getDistance [this s1 s2] [this s1 s2 m-weight]))
;;in some namespace y that refers all vars from x
(extend-type String
IStemmable
(stem
([this] (stem this "english"))
([this lang]
(let [stemmer (help/porter-stemmer lang)]
(doto stemmer
(.setCurrent this)
(.stem))
(.getCurrent stemmer))))
(getRoot [this _ dic] (get dic this "NOT-FOUND!"))
IDistance
(getDistance
([this other]
(help/levenshtein-distance* this other)) ;;delegate to
helper fn
([this other mismatch-weight]
(help/levenshtein-distance* this other mismatch-weight))))
;;same
here
(defrecord PorterStemmer [lang input output] ;;COMPILES AND WORKS
FINE -
NO PROBLEMS
IStemmable
(stem [_ token]
(stem token lang)) ;;delegate to String for this
(getRoot [_ token dic]
(getRoot token _ dic)))) ;;delegate to String for this
(defrecord LevenshteinDistance [] ;;DOESN'T COMPILE
IDistance
(getDistance [_ s1 s2]
(getDistance s1 s2)) ;;delegate to String for this
(getDistance [_ s1 s2 weight]
(getDistance s1 s2 weight))) ;;and this
trying to load the file results in:
CompilerException java.lang.IllegalArgumentException: No single
method:
getDistance of interface: cluja.protocols.IDistance found for
function:
getDistance of protocol: IDistance,
compiling:(/media/sorted/uni_stick/cluja/src/cluja/concretions/models.clj:152:3)
What am I doing wrong? I am practically doing the exact same thing
for
these 2 protocols. Both of them delegate to the implementations
extended
to string. I keep looking at the code and I see nothing wrong! The
'getDistance' with 3 args delegates to the one with 2 and the one
with 4
delegates to the one with 3 (from String)...even more confusingly why
one works and the other complains? any ideas/insights?
Jim
--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient
with your first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.