As part of my exploration of Ruby, I attended Francis Hwang’s presentation to the New York Linux Users’ Group. One feature that caught my interest in his talk was the OpenStruct class, which lets you assign values to arbitrary “slots” within an object.
require 'ostruct' me = OpenStruct.new me.name = "Stuart Sierra" me.net_worth = -500
Now me.name
returns “Stuart Sierra” and me.net_worth
returns -500. The clever thing about this is that nowhere did I define name
or net_worth
as member variables or methods of a class. They just spring into being in the OpenStruct
instance as soon as I use them. This is great for developing new data structures before you’re entirely sure what slots you’ll need.
Objects in JavaScript always work this way. JavaScript is prototype-based, so there are no classes, just objects. Each object effectively has its own unique namespace in which you can create new slots simply by using them.
Common Lisp can do this too. Every CL symbol has a property list that can be accessed with get
.
CL-USER> (defvar me) ME CL-USER> (setf (get 'me 'name) "Stuart Sierra") "Stuart Sierra" CL-USER> (get 'me 'name) "Stuart Sierra"
What I find curious is that this feature of Common Lisp is totally distinct from the object system. Object slots are not symbol properties, just as symbols are not objects (they are bound to objects). But one could make a passable prototype-based object system using just symbol properties and function closures. I think it’s one of those places that shows how Common Lisp grew from many different sources, resulting in similar but distinct ways of doing the same thing. Peter Seibel demonstrates that it can be useful to have both, by using symbol properties to store bookkeeping information needed by macros that generate new classes.