Object system for Clojure?

161 views
Skip to first unread message

Jan Rychter

unread,
Jan 30, 2009, 1:09:35 PM1/30/09
to clo...@googlegroups.com
There is something I don't understand about Clojure. There are
multimethods (great), but I can't seem to find objects they can operate
on. I come to Clojure from a Common Lisp background, having done a fair
bit of CLOS programming. Mind you, I am not necessarily a fan of overly
complex object hierarchies. But still -- I've been trying and I just
can't seem to make objects in Clojure work.

From what I read, the reasoning is that Clojure provides extremely
general multimethods where you have to define a dispatch function. On
the object side, there is metadata and you can do with it whatever you
want. But this seems to leave a gaping hole saying "fill me with an
object system". Is the goal for everyone to roll their own?

I'm against overusing objects, but there are things which quite
naturally map into the object-oriented paradigm. Let's take windowing
systems for example -- let's assume you have various kinds of windows,
each with its own unique attributes and attributes which are inherited
(e.g. a window-id attribute is common to all windows). I can't see how
you'd implement that in Clojure when you can't inherit structmap
attributes. How do I build my-special-window which is a kind of window
and also inherits window's window-id attribute? I know how to inherit
behavior, but there is behavior that requires data, and I have no idea
how to store and inherit that data. I looked at merge, but it seems you
can't merge structure basis objects.

Unfortunately, all discussion on Clojure polymorphism, multimethods and
objects that I could find seems to use overly simplified examples (it
really only shows that there are multimethods which can do multiple
dispatch on anything), and doesn't really help. Even Stuart Halloway's
beta "Programming Clojure" book has very simple examples that don't
inherit attributes, just use different type tags for the same
information.

--J.

David Nolen

unread,
Jan 30, 2009, 1:51:58 PM1/30/09
to clo...@googlegroups.com
At this point you have to roll your own, I have an experimental thing I plan on fleshing out temporarily called CLJOS. I've implemented field inheritance, type inheritance, basic multiple inheritance, basic introspection.  Search the mailing list for CLJOS and you'll see more details.  It only took 200 lines of code

You mention in particular field inheritance.  In CLJOS, you can define default initializers for fields.  When you inherit from a class(es), a look up table of default initializers is consulted, the values are merged in the direction of the inheritance chain.

Admittedly, it is a bit of a toy example at this point, but it shows that without too much effort you could build a very powerful object system based on structs, and I've already found CLJOS to useful in my own hacking.  In fact I would be happy to collaborate on something more substantial.


Examples here and here:

Not too many people are doing heavy or fancy GUI related stuff (as far as I can tell) so I can understand why this hasn't come up often.  I note that you work with weblocks so I can see where you're coming from and I'm glad you're bringing it up.

That said, I imagine if a decent, useful, and complete lightweight object system could be designed I'm sure it could get rolled into clojure-contrib without too much brouhaha.

Stuart Sierra

unread,
Jan 30, 2009, 3:16:53 PM1/30/09
to Clojure
On Jan 30, 1:09 pm, Jan Rychter <j...@rychter.com> wrote:
> From what I read, the reasoning is that Clojure provides extremely
> general multimethods where you have to define a dispatch function. On
> the object side, there is metadata and you can do with it whatever you
> want. But this seems to leave a gaping hole saying "fill me with an
> object system". Is the goal for everyone to roll their own?

I think the goal is to provide object-like capabilities without
needing actual objects. The emerging pattern is to use maps, with
a :tag key identifying the type. Types are usually namespace-
qualified keywords. You can create any kind of inheritance hierarchy
with "derive".

(defn make-window [id]
{:tag ::window, :id id})

(defn make-color-window [id color]
(assoc (make-window id)
:tag ::color-window
:color color))

(derive ::color-window ::window)

(defmulti describe-window :tag)

(defmethod describe-window ::window [w]
(println "Window with ID" (:id w)))

(defmethod describe-window ::color-window [w]
(println (:color w) "Window with ID" (:id w)))

(let [w (make-color-window 24 "blue")]
(describe-window w))
;; => prints "blue window with ID 24"

-Stuart Sierra

Graham Fawcett

unread,
Jan 30, 2009, 3:25:45 PM1/30/09
to clo...@googlegroups.com
On Fri, Jan 30, 2009 at 1:09 PM, Jan Rychter <j...@rychter.com> wrote:

> From what I read, the reasoning is that Clojure provides extremely
> general multimethods where you have to define a dispatch function. On
> the object side, there is metadata and you can do with it whatever you
> want. But this seems to leave a gaping hole saying "fill me with an
> object system". Is the goal for everyone to roll their own?

Hi,

Just curious, have you seen the "a la carte hierarchies" scheme that
Rich worked up last summer?

http://groups.google.com/group/clojure/browse_frm/thread/9cc9926de1bdb128?pli=1

Best,
Graham

Colin Walters

unread,
Jan 30, 2009, 3:56:04 PM1/30/09
to Clojure
On Jan 30, 3:16 pm, Stuart Sierra <the.stuart.sie...@gmail.com> wrote:
>
> I think the goal is to provide object-like capabilities without
> needing actual objects.

Why is that the goal? I mean, the JVM provides a well defined, high
performance object oriented system. Clojure can already generate
classes - the main issue I see looking at the API is that it doesn't
look convenient to make "property bag" classes.

There could be an API like: (class Window :properties [height width])
That could auto-generate constructors and property accessors too. You
could also have a flag like :immutable which would prevent it from
generating setters, and that would fit in well with Clojure's general
immutability.

David Nolen

unread,
Jan 30, 2009, 4:16:31 PM1/30/09
to clo...@googlegroups.com
(defn make-window [id]
 {:tag ::window, :id id})

(defn make-color-window [id color]
 (assoc (make-window id)
   :tag ::color-window
   :color color))

(derive ::color-window ::window)

(defmulti describe-window :tag)

(defmethod describe-window ::window [w]
 (println "Window with ID" (:id w)))

(defmethod describe-window ::color-window [w]
 (println (:color w) "Window with ID" (:id w)))

(let [w (make-color-window 24 "blue")]
 (describe-window w))
;; => prints "blue window with ID 24"

In any fairly large UI system this would become tedious in no time and a hit on readability.  For example consider building a UI system from scratch for a JOGL game of even medium complexity in pure Clojure (scrollbars, views, zoomable views, sprites, physics, icons, buttons, et al.). A higher level abstraction is required.  I'm not arguing that the language needs to provide it directly, but at least have the framework in place so that it can be done via macros (which already is the case).

David Nolen

unread,
Jan 30, 2009, 4:19:38 PM1/30/09
to clo...@googlegroups.com
On Jan 30, 3:16 pm, Stuart Sierra <the.stuart.sie...@gmail.com> wrote:
>
> I think the goal is to provide object-like capabilities without
> needing actual objects.

Why is that the goal?  I mean, the JVM provides a well defined, high
performance object oriented system.  Clojure can already generate
classes - the main issue I see looking at the API is that it doesn't
look convenient to make "property bag" classes.

Except that compared to Clojure's multihierarchy multimethod system, Java's object system seems a bit, shall we say, weak.  And why program in Java when you can program in Clojure? Why deal with Java UI interop if you don't need/want it? Just saying the OP has a valid point. 

Perhaps someone wants to build a web based UI framework along the lines of weblocks.  Perhaps you want to define easily extendable widgets in this UI framework...

Jan Rychter

unread,
Jan 31, 2009, 6:44:04 AM1/31/09
to clo...@googlegroups.com
Stuart Sierra <the.stua...@gmail.com> writes:
> On Jan 30, 1:09 pm, Jan Rychter <j...@rychter.com> wrote:
>> From what I read, the reasoning is that Clojure provides extremely
>> general multimethods where you have to define a dispatch function. On
>> the object side, there is metadata and you can do with it whatever you
>> want. But this seems to leave a gaping hole saying "fill me with an
>> object system". Is the goal for everyone to roll their own?
>
> I think the goal is to provide object-like capabilities without
> needing actual objects. The emerging pattern is to use maps, with
> a :tag key identifying the type. Types are usually namespace-
> qualified keywords. You can create any kind of inheritance hierarchy
> with "derive".
>
> (defn make-window [id]
> {:tag ::window, :id id})
>
> (defn make-color-window [id color]
> (assoc (make-window id)
> :tag ::color-window
> :color color))
[...]

Let's focus on this part for now. As I mentioned, I do understand
multimethods and derive, and my post was not about them. In fact, I
noticed that multimethods and ad-hoc hierarchies are the only thing
people discuss, forgetting totally about attribute inheritance.

Phrased differently, I don't understand the utility of StructMaps. If
your example above is the preferred way to create objects that inherit
attributes from other objects, then this means inheritance is instance
(or prototype) based. I can extend the set of keys using assoc on a
particular instance.

But if that is so, what's the point of having StructMaps? You can use
defstruct to define your base object, but you can't use it to define
objects that extend the set of attributes (keys) -- you can only do that
to instances. So while I can use defstruct to define my base window
object and use struct-map afterwards, I can't define color-window this
way -- I always have to call make-color-window to create a color-window.

Let's follow the StructMap example from http://clojure.org/data_structures
-- I guess what I'm looking for is a way to do this (whatever the
actual syntax ends up being):

(defstruct desilu () (:tag ::desilu) :fred :ricky)
(defstruct extended-desilu (::desilu) (:tag ::extended-desilu) :lucy :ethel)

... instead of extending the set of attributes of a particular instance.

I guess I would be fine with delegating all responsibility to
constructor functions, but then I don't understand the point of having
StructMaps.

I have a nagging feeling that this amount of liberty will result in many
mutually incompatible object systems being created.

Another thing which I found very useful in CLOS were method
combinations, in particular the standard method combination with
:before, :after and :around method types. I am not sure if that is
implementable using the current Clojure multimethod system.

Any pointers or hints are very welcome.

--J.

Jan Rychter

unread,
Jan 31, 2009, 6:51:23 AM1/31/09
to clo...@googlegroups.com
David Nolen <dnolen...@gmail.com> writes:
> At this point you have to roll your own, I have an experimental thing I plan
> on fleshing out temporarily called CLJOS. I've implemented field
> inheritance, type inheritance, basic multiple inheritance, basic
> introspection. Search the mailing list for CLJOS and you'll see more
> details. It only took 200 lines of code
[...]

This is interesting, thanks!

> Not too many people are doing heavy or fancy GUI related stuff (as far as I
> can tell) so I can understand why this hasn't come up often. I note that
> you work with weblocks so I can see where you're coming from and I'm glad
> you're bringing it up.

Yes, you get the same issue when writing a widget-based web framework. I
mean, I know functional programming is all the rage now, but there are
things which really are very well represented by objects and
inheritance, and GUI elements and web page widgets belong to that
category. Every widget has a dom-id and dom-class, for example, while
certain widgets also have other attributes.

> That said, I imagine if a decent, useful, and complete lightweight object
> system could be designed I'm sure it could get rolled into clojure-contrib
> without too much brouhaha.

That's what I was curious about -- I wanted to know whether this is the
desired outcome.

Also, before someone brings that up: I believe CLOS is overly
complex. I don't necessarily want all of CLOS+MOP in Clojure. Still, I
think there is a reasonable compromise to be found somewhere.

--J.

Meikel Brandmeyer

unread,
Jan 31, 2009, 7:24:54 AM1/31/09
to clo...@googlegroups.com
Hi,

Am 31.01.2009 um 12:44 schrieb Jan Rychter:

> Phrased differently, I don't understand the utility of StructMaps.

At the moment struct-maps are an optimisation for the
case that you have a lot of maps of the same type.
struct-maps share there key information. No more, no less.

Sincerely
Meikel

Reply all
Reply to author
Forward
0 new messages