What's a convenient way of calling super.method()?

83 views
Skip to first unread message

CuppoJava

unread,
Mar 21, 2009, 4:38:37 PM3/21/09
to Clojure
Hi,
I'm making some heavy use of multi-methods and proxies in my code, and
I'm wondering if there's a convenient way of calling "superclass"
methods, both for multi-methods and for proxies.

For multi-methods, I'm currently doing:

(mymethod (assoc object :tag :super-class))
which is a little clumsy.

For proxies, I haven't figured out a way yet.

Thanks for your help
-Patrick

Mark Engelberg

unread,
Mar 21, 2009, 5:02:02 PM3/21/09
to clo...@googlegroups.com
On Sat, Mar 21, 2009 at 1:38 PM, CuppoJava <patrick...@hotmail.com> wrote:
> (mymethod (assoc object :tag :super-class))
> which is a little clumsy.

Not only is it clumsy, but if mymethod returns a fresh object that is
based off of this object in some way (e.g., a non-destructive
"setter"), the return value will have this super-class tag rather than
the original, which is probably not the desired behavior.

Stuart Sierra

unread,
Mar 22, 2009, 12:10:38 PM3/22/09
to Clojure
On Mar 21, 4:38 pm, CuppoJava <patrickli_2...@hotmail.com> wrote:
> For proxies, I haven't figured out a way yet.

Proxies cannot call superclass methods. Classes generated with gen-
class can. However, if you regularly need to call superclass methods
instead of subclass methods, then you may want to rethink your design,
perhaps by using different names for the "super" and "sub" methods.

-Stuart Sierra

pmf

unread,
Mar 22, 2009, 12:37:59 PM3/22/09
to Clojure
I think they can, using proxy-super.

CuppoJava

unread,
Mar 22, 2009, 4:17:16 PM3/22/09
to Clojure
Thanks for the responses:

I read into proxy-super, and it fulfills my needs.

But I'm worried about my current approach for multi-methods now:
As Mark put it:
"Not only is it clumsy, but if mymethod returns a fresh object that
is
based off of this object in some way (e.g., a non-destructive
"setter"), the return value will have this super-class tag rather
than
the original, which is probably not the desired behavior. :

Which is a serious problem that hasn't occurred to me before.

So, considering it seems that not many other people have run into this
issue, can I assume that most people just haven't had a need to call a
super multi-method? Is it a bad design choice to call your inherited
methods?

Thanks for the help
-Patrick

Mark Engelberg

unread,
Mar 23, 2009, 4:14:20 AM3/23/09
to clo...@googlegroups.com
On Sun, Mar 22, 2009 at 1:17 PM, CuppoJava <patrick...@hotmail.com> wrote:
> So, considering it seems that not many other people have run into this
> issue, can I assume that most people just haven't had a need to call a
> super multi-method? Is it a bad design choice to call your inherited
> methods?

I haven't needed to do this, although it's the kind of thing I would
expect to be able to do. I was kind of surprised when I noticed the
other day that "super" wasn't a part of the multi-method system. I'd
also be curious to know if there are coding patterns that avoid this
altogether, because as far as I know, it's a pretty essential idea
when dealing with inheritance.

CuppoJava

unread,
Mar 24, 2009, 2:41:29 PM3/24/09
to Clojure
*bump*

Just letting people know that this is still an important and
unresolved issue for me.

Calling super multi-methods?

David Nolen

unread,
Mar 28, 2009, 9:48:57 PM3/28/09
to clo...@googlegroups.com
Here's a quickly hacked together solution (code golf welcome), I'm using special variables so that we don't affect what other multimethods see:

(def *super* nil)

(defmacro super [afn obj & args]
  (let [fn-key (keyword (str afn))]
  `(binding [*super* {~fn-key (or (and (~fn-key *super*)
      (first (parents (~fn-key *super*))))
 (first (parents (type ~obj))))}]
     ~(if args 
`(apply ~afn [~obj ~@args])
`(apply ~afn [~obj])))))

(defn dispatch-for [fn-sym]
  (let [fn-key (keyword (str fn-sym))]
    (fn [x]
      (if-let [super-type (fn-key *super*)]
super-type
(type x)))))

(defmulti fnA (dispatch-for 'fnA))
(defmethod fnA ::typeA
  [x]
  (println "Calling fnA, the type of x is" (type x))
  (fnB x))

(defmethod fnA ::typeB
  [x]
  (println "Calling super")
  (super fnA x))

(defmulti fnB type)
(defmethod fnB ::typeA
  [x]
  (println "Calling fnB the type of x is" (type x)))

(derive ::typeB ::typeA)
(fnA (with-meta {} {:type ::typeA}))
(fnA (with-meta {} {:type ::typeB}))

---------------> Output
Calling fnA, the type of x is :user/typeA
Calling fnB the type of x is :user/typeA
Called super
Calling fnA, the type of x is :user/typeB
Calling fnB the type of x is :user/typeB

Cheers,
David

CuppoJava

unread,
Mar 29, 2009, 8:20:46 AM3/29/09
to Clojure
Thanks a lot for that David,
It works perfectly for me.

Are there any circumstances where it doesn't work? I haven't run into
any yet, but if there are, I'll design my program around it.
-Patrick

David Nolen

unread,
Mar 29, 2009, 10:33:39 AM3/29/09
to clo...@googlegroups.com
Glad to be of help. To be totally honest I hadn't really tested it too much, so I don't know ;) One obvious limitation here is that it doesn't work with multiple inheritance (it only looks at the first item in the parents set).  As long you're sticking with a Java-style single inheritance model this doesn't really present much of a problem. I'll clean it up when I have some extra time and add some test cases, provide a small readme listing limitations, and host it on GitHub for anyone else that needs this behavior.

David
Reply all
Reply to author
Forward
0 new messages