Inheritance & multiple inheritance using structs

19 views
Skip to first unread message

David Nolen

unread,
Jan 18, 2009, 9:47:21 PM1/18/09
to clo...@googlegroups.com
After a couple of days of hacking it's clear that Clojure can support OO structures really, really, well.  I've whipped up a little thing, jokingly called CLJOS.  I'm curious to know what people think.  It's a fairly simple affair combining structs, hierarchies, and some helper functions.

It's my first attempt at Clojure programming so I'm sure it could use some cleanup, but it works.  Of course open to any thoughts improvements etc.  In particular I'm using refs to deal with changes to the hierarchy and the class initializers.

You can compose structs, here's a very nonsensical example:

;; multiple inheritance
(defclass beaver [object]
  (:tail true))
(defclass duck [object]
  (:bill true))
(defclass platypus [beaver duck]
  (:marsupial true))

(def aplatypus (make-instance platypus))
(isaa? aplatypus ::duck) ;; yield true
(isaa? aplatypus ::beaver) ;; yields true

isaa? takes an instance and matches it against a fully namespaced keyword.

It supports default values for keys, they are overridden according to the class hierarchy.

(defclass shape [object]
  (:position [0 0]))

(defclass circle [shape]
  (:radius 10))

(make-instance circle :raduis 20]) ;; creates struct -> {:position [0 0], :tag :cljos-example/circle, :radius 20}

And it support a simple system for calling multimethods up the inheritance chain simply by re-assigning the :tag.

(defmulti area :tag)

(defmethod area ::shape [s]
  (print "Abstract method!\n"))

(defmethod area ::rect [r]
  (print "Area of rect\n")
  (area (super r))
  (* (:width r) (:height r)))


Of course it blows my mind that this can be done in less than 100 lines of code (Clojure is amazing!). It seems to me a decent way to deal with OO style coding if you want to stick with pure clojure and don't need the Java interop of gen-class.

David Nolen

unread,
Jan 19, 2009, 1:38:58 AM1/19/09
to clo...@googlegroups.com
Of course it might be the case that not many people are interested in the implementing ideas from CLOS for Clojure especially since there's a lot of exciting new functional ground to cover in Clojure first ;)  I come from a UI background so I'm interested in the OO implications/possiblities of Clojure.

In any case a couple half-thought out notes about make-instance performance characteristics:

make-instance is about as fast as struct-map even tho it dynamically looks up key initializers (make sense since it's just a macro that unfolds into a struct-map).

On a new MBP 2.4ghz you can allocate 1,000,000 instances (3 fields) in ~700ms.  Same for struct-map.  struct is a little less than 4 times faster it seems that struct-map. One possible optimization for make-instance would be to make make-instance automatically order all keys (seems like a bit of work especially with multiple inheritance).  As a comparison using a hash-map directly gives another 3-4x performance increase.

As a comparison of other implementations (probably not a fair one since CLOS does way more bookkeeping than my measly hack ;):

SBCL instantiates 1,000,000 instances of a class with 3 fields in ~70ms (could get closer to ~250ms in Clojure with auto-ordering of keys, if the sorting algorithm was quick enough).
Clozure CL 64 does the same in ~1.5secs.

Cool stuff.  Clojure is zippy.

Emeka

unread,
Jan 19, 2009, 7:30:24 AM1/19/09
to clo...@googlegroups.com


Great job!

Emeka

evins...@gmail.com

unread,
Jan 22, 2009, 10:55:49 PM1/22/09
to Clojure


On Jan 19, 12:38 am, David Nolen <dnolen.li...@gmail.com> wrote:
> Of course it might be the case that not many people are interested in the
> implementing ideas from CLOS for Clojure

It's definitely interesting. I'd like to have eql specializers and the
ability to build hierarchies of arbitrary types (e.g. a hierarchy of
strings for one particular application I'm working on), so any hacking
around with CLOS-like dispatching and specializers is interesting to
me.


aria42

unread,
Jan 23, 2009, 12:00:11 AM1/23/09
to Clojure
[sorry for duplicating content from the email i sent you David]

Would anyone be interested in simplifying some of the boilerplate for
defining methods in clojs? Currently you have to do this,

(defclass circle [shape]
(:radius 10))
(defmulti area :tag)
(defmethod area ::circle [this] (* (:radius this) (:radius this) (Math/
PI)))

But it seems like 99% of the time this is boilerplate b/c you want
multi-methods to dispatch on :tag, so we should maybe have a shortcut
to let us do this...

(defclass circle [shape]
(:radius 10)
(defmethod area [] (* :radius :radius Math/PI)))

I'm not a macro-wiz but it must be possible to alter that defmethod to
check if it's defined and to wrap the symbol refrences with (:symbol
this) and cons this to the argument list.

Just a thought, Aria

On Jan 22, 7:55 pm, "evins.mi...@gmail.com" <evins.mi...@gmail.com>
wrote:
Reply all
Reply to author
Forward
0 new messages