extend-type on

54 views
Skip to first unread message

Phillip Lord

unread,
Aug 2, 2013, 6:44:32 AM8/2/13
to clo...@googlegroups.com

I want to use extend-type to support a protocol both on a class and it's
subclasses. But I don't know how to do a superclass call. So for
instance, with this code....

(defprotocol PThree
(hello [this]))

(extend-type
Number PThree
(hello [this]
(if (= 10 this)
"hello")))

(extend-type
Long PThree
(hello [this]
(if (= 5 this)
"goodbye")))

;; this return "goodbye"
(hello 5)
;; this returns nil
(hello 10)


I really want (hello 10) to return "hello". But the extend-type of Long
takes precedence. How can I call the implementation on the superclass
instead?

Phil

Jim - FooBar();

unread,
Aug 2, 2013, 6:55:59 AM8/2/13
to clo...@googlegroups.com
your extension point on Number is never fired because 10 is a Long.
Generally speaking extending protocols to interfaces is not
suggested...I've been bitten a couple of times in particular whenever
I'm extending to 2 different interfaces that *are* related...You can
certainly do it but you have to have sure that your top interface comes
first than the others.

Your example is actually fine...try "(hello (int 10))" - you should see
"hello"

Jim

Jim - FooBar();

unread,
Aug 2, 2013, 7:02:47 AM8/2/13
to clo...@googlegroups.com
Moreover, if you're going to extend a particular protocol to many types, it's better to use 'extend-protocol' which expands to a bunch of 'extend-type' forms...if you need a point of reference I recently  finished extending a 'DataSet' protocol to all clojure/java data-structures. YOu can find it here :  https://github.com/jimpil/hotel-nlp/blob/master/src/hotel_nlp/tools/normalito/core.clj

500 lines of pure protocol extension points! It is that long because a requirement of mine was not to output the same type as the input.

Jim

Phillip Lord

unread,
Aug 2, 2013, 7:24:07 AM8/2/13
to clo...@googlegroups.com
"Jim - FooBar();" <jimpi...@gmail.com> writes:
> your extension point on Number is never fired because 10 is a Long.

Sure, I understand why it's not working! I just don't know how to fix it.

> Generally speaking extending protocols to interfaces is not
> suggested...

I'm extending an API which is interface driven -- the OWL API in this
case.

http://owlapi.sourceforge.net/javadoc/index.html

At the moment I am just playing, but the idea was to extend this API so
that it supports core.match.

> I've been bitten a couple of times in particular whenever
> I'm extending to 2 different interfaces that *are* related...You can
> certainly do it but you have to have sure that your top interface
> comes first than the others.

In this case, that wouldn't work; I really need to call a superclass
implementation.

For instance, the class OWLEntity has one method on it that I need to
call. It has 8 direct subintefaces, and quite a lot more below that. I
don't want to have to support the single method multiple times.

I can think of otherways to do this, I guess, but they kind of involve
lots of instance? checks.

Phil

Meikel Brandmeyer (kotarak)

unread,
Aug 2, 2013, 7:39:05 AM8/2/13
to clo...@googlegroups.com
Hi,


Am Freitag, 2. August 2013 13:24:07 UTC+2 schrieb Phillip Lord:
"Jim - FooBar();" <jimpi...@gmail.com> writes:
> your extension point on Number is never fired because 10 is a Long.

Sure, I understand why it's not working! I just don't know how to fix it.


Plain old functions:

    (defn number-hello
      [n]
      (when (= n 10)
        "hello"))

    (defn long-hello
      [n]
      (if (= n 5)
        "goodbye"
        (number-hello n)))

    (extend Number
      PThree
      {:hello number-hello})

    (extend Long
      PThree
      {:hello long-hello})

    (extend Integer
      PThree
      {:hello long-hello})


HTH.

Kind regards
Meikel

Jim - FooBar();

unread,
Aug 2, 2013, 7:46:56 AM8/2/13
to clo...@googlegroups.com
On 02/08/13 12:24, Phillip Lord wrote:
> "Jim - FooBar();" <jimpi...@gmail.com> writes:
>> your extension point on Number is never fired because 10 is a Long.
> Sure, I understand why it's not working! I just don't know how to fix it.

there is nothing to fix in this particular example...everything works
as expected.

>
>> Generally speaking extending protocols to interfaces is not
>> suggested...
> I'm extending an API which is interface driven -- the OWL API in this
> case.
all well-designed APIs are abstraction driven. I imagine there nothing
special about OWL in that respect...
take for instance the openNLP API...It has 4 or 5 interfaces that define
the different components that openNLP supports (Tokenizer, Stemmer,
NameFinder etc etc).
I can easily extend-type to all these interfaces (that are all on the
same 'level') which allows me to cover all concrete classes. This is one
of the cases where it works for me just fine...

However if I try to extend a protocol to java.util.Collection and then
to clojure.lang.IPersistenVector it won't work...only the extension to
java.util.Collection fires (because it is above
clojure.lang.IPersistenVector). That is what I meant...


>
> http://owlapi.sourceforge.net/javadoc/index.html
>
> At the moment I am just playing, but the idea was to extend this API so
> that it supports core.match.
>
>> I've been bitten a couple of times in particular whenever
>> I'm extending to 2 different interfaces that *are* related...You can
>> certainly do it but you have to have sure that your top interface
>> comes first than the others.
> In this case, that wouldn't work; I really need to call a superclass
> implementation.
>
> For instance, the class OWLEntity has one method on it that I need to
> call. It has 8 direct subintefaces, and quite a lot more below that. I
> don't want to have to support the single method multiple times.
I can see from the docs that OWLEntity is an interface and as you say it
has several subinterfaces. My approach would be to either extend only to
OWLEntity or all of the subinterfaces. If you try to do both, I suspect
only the top one will always be fired...If the implementation of the
method you want is identical for all, you can just bother with
OWLEntity or programmatically emit identical extension points for all
subinterfaces.

>
> I can think of otherways to do this, I guess, but they kind of involve
> lots of instance? checks.

apart from sounding like non-idiomatic code, it also sounds like
problematic code since you're dealing with interfaces that are related...
user=> (instance? Number 1)
true
user=> (instance? Long 1)
true
user=> (instance? Object 1)
true


>
> Phil
>

Jim

Jim - FooBar();

unread,
Aug 2, 2013, 8:06:51 AM8/2/13
to clo...@googlegroups.com
On 02/08/13 12:46, Jim - FooBar(); wrote:
> or programmatically emit identical extension points for all
> subinterfaces/subclasses

;;adopted from clojure/core/protocols.clj

(def ^:private co-stub
'(run [this text]
(if (instance? edu.stanford.nlp.pipeline.Annotation text)
(do (.annotate this text) text)
(let [ann (edu.stanford.nlp.pipeline.Annotation. ^String text)]
(.annotate this ann) ann))))

(defn- emit-IComponent-impls* [syms]
(mapcat
(fn [s]
[(symbol (str "edu.stanford.nlp.pipeline." s)) co-stub])
syms))

(defmacro ^:private emit-IComponent-impls [& syms]
`(extend-protocol IComponent
~@(emit-IComponent-impls* syms)))

and finally you call it like this:

(emit-IComponent-impls ;nice trick to avoid enumerating all the
identical implementations
POSTaggerAnnotator PTBTokenizerAnnotator WordsToSentencesAnnotator
TokenizerAnnotator
CleanXmlAnnotator MorphaAnnotator NERCombinerAnnotator RegexNERAnnotator
TrueCaseAnnotator ParserAnnotator DeterministicCorefAnnotator)

neat trick imo :)

Jim

Phillip Lord

unread,
Aug 2, 2013, 9:06:39 AM8/2/13
to clo...@googlegroups.com
"Meikel Brandmeyer (kotarak)" <m...@kotka.de> writes:
>> Sure, I understand why it's not working! I just don't know how to fix it.
>>
>>
> Plain old functions:
>
> (defn number-hello
> [n]
> (when (= n 10)
> "hello"))
>
> (defn long-hello
> [n]
> (if (= n 5)
> "goodbye"
> (number-hello n)))
>
> (extend Number
> PThree
> {:hello number-hello})
>
> (extend Long
> PThree
> {:hello long-hello})
>
> (extend Integer
> PThree
> {:hello long-hello})
>
> HTH.

It does, and appears to be the only way to go.

Phil

Meikel Brandmeyer

unread,
Aug 2, 2013, 9:43:40 AM8/2/13
to clo...@googlegroups.com
You could spice things a bit up:

    (defn extend-to
      [protocol & class-impl-mappings]
      (doseq [[class impl] (partition 2 class-impl-mappings)]
        (extend class protocol impl)))

    (extend-to PThree
      Long    {:hello long-hello}
      Integer {:hello long-hello}
      Number  {:hello number-hello})


Meikel



2013/8/2 Phillip Lord <philli...@newcastle.ac.uk>
--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@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+u...@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 a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/4y5eJUHL3II/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



Phillip Lord

unread,
Aug 2, 2013, 9:49:56 AM8/2/13
to clo...@googlegroups.com

Well, this is certainly a possibility. Especially if I do it at
compilation time, I can put the match functions for each class into an
array, and then combine them by climbing the class hierarchies.

Phil


"Jim - FooBar();" <jimpi...@gmail.com> writes:
> On 02/08/13 12:46, Jim - FooBar(); wrote:
>> or programmatically emit identical extension points for all
>> subinterfaces/subclasses
> (def ^:private co-stub
> '(run [this text]
> (if (instance? edu.stanford.nlp.pipeline.Annotation text)
> (do (.annotate this text) text)
> (let [ann (edu.stanford.nlp.pipeline.Annotation. ^String text)]
> (.annotate this ann) ann))))
>
> (defn- emit-IComponent-impls* [syms]
> (mapcat
> (fn [s]
> [(symbol (str "edu.stanford.nlp.pipeline." s)) co-stub])
> syms))
>
> (defmacro ^:private emit-IComponent-impls [& syms]
> `(extend-protocol IComponent
> ~@(emit-IComponent-impls* syms)))
>
> and finally you call it like this:
>
> (emit-IComponent-impls ;nice trick to avoid enumerating all the identical
> implementations
> POSTaggerAnnotator PTBTokenizerAnnotator WordsToSentencesAnnotator
> TokenizerAnnotator
> CleanXmlAnnotator MorphaAnnotator NERCombinerAnnotator RegexNERAnnotator
> TrueCaseAnnotator ParserAnnotator DeterministicCorefAnnotator)
>
> neat trick imo :)
>
> Jim
>
> --

--
Phillip Lord, Phone: +44 (0) 191 222 7827
Lecturer in Bioinformatics, Email: philli...@newcastle.ac.uk
School of Computing Science, http://homepages.cs.ncl.ac.uk/phillip.lord
Room 914 Claremont Tower, skype: russet_apples
Newcastle University, twitter: phillord
NE1 7RU
Reply all
Reply to author
Forward
0 new messages