Closures + Hash Tables = As Much OOP as You’ll Ever Need

“In another thirty years people will laugh at anyone who tries to invent a language without closures, just as they’ll laugh now at anyone who tries to invent a language without recursion.” — Mark Jason Dominus

You might be wondering what all of the talk about closures is about, really. You might be scratching your head at the boring examples that just run a simple little counter inside a function. You might be wondering how functional programming can be more fundamental than (and even able to subsume) object oriented programming. You might be disappointed that Mark Jason Dominus hasn’t gotten his book up on-line, yet. You might be willing to settle for a quick dumb example in Common Lisp written by an average developer. If any of the previous guesses are right, you came to the right place.

In this text file, you’ll find a mere 16 13 lines of code that define a small chunk of OOP in Common Lisp. The code should not only be understandable to beginners, but it was actually written by a beginner– so it should also hopefully give an indication of how easy it is to build “new” fundamental-ish programming concepts onto Common Lisp.

An “obi,” as I define it here, is somewhat like an object, but there are some key differences. (I call my version of an object an “obi” because I’m implementing just a very small piece of OOP.) An “obi” is a hash table wrapped up in a closure. The closure provides one of the main pillars of OOP: encapsulation. Nothing in your program can mess with the variables inside of closure without interacting through the function call that the closure provides. The hash table provides a place to store all of your properties and methods. Because Lisp is dynamically typed and because functions are first-class objects, there’s really no need to make a difference between properties and methods. In an “obi,” functions are just one of the many information types that can be stored in the hash table.

In the example below, we see an obi being used from the REPL prompt. Its color and shape properties are set and also a function called add is put in and called. The “.” is used in Common Lisp to notate a dotted list, so I used ~ to represent “get”, ~> to represent the create/set a property/method, and ~! is used to represent a call to an obi’s function:

; SLIME: The Superior Lisp Interaction Mode for Emacs
CL-USER> (setf f (create-obi))
#<COMPILED-CLOSURE CREATE-OBI-1>
CL-USER> (~> f ‘color ‘red)
RED
CL-USER> (~> f ‘shape ‘square)
SQUARE
CL-USER> (~ f ‘color)
RED
T
CL-USER> (~ f ‘shape)
SQUARE
T
CL-USER> (~> f ‘add #’+)
#<SYSTEM-FUNCTION +>
CL-USER> (~! f ‘add ‘(1 2 3 4 5 6 7))
28

Obi’s lack any form of “type”– in fact, you can add additional properties and methods to them at any time. Even if you don’t intend to use Common Lisp in a production application, this is an option that should be in your mental tool box. You may not always need to resort to using inheritance or subclassing to create a variation of an object you defined. If you think a little bit more functionally, you can abstract out the bulk of the design and then allow other developers to customize its use by setting or overriding some of the key functions. Of course, in a wide-open typeless setting, that’s a feature you get whether you think you want it or not!

For a much more elegant and substantive example of how to implement OOP in Common Lisp with an insanely small amount of code, see Paul Graham’s Ansi Common Lisp.

PS If anyone knows how to trick WordPress into letting me display Lisp code without destroying the indenting, please let me know.

PPS Looking at the search engine traffic coming in here, I see that (besides all of the people trying to figure out carriage returns in Emacs) there are a few coming to the site for stuff like “lisp closures,” “oop in lisp” and “lisp hash tutorial.” Hopefully this post will provide a little more substance for those folks than the previous drivel I’ve spewed forth here in my clumsy efforts to get used to some of these new ideas.

PPPS Please forgive the hyperbole in the title.

——————————

Related Articles:

Closures + Lambda = The key to OOP in Lisp

Closures, Objects, Forklifts, and the quest for truth

24 Responses to “Closures + Hash Tables = As Much OOP as You’ll Ever Need”

  1. Ramon Leon Says:

    Try wrapping your code in pre tags

    (it should
    (indent
    (just fine)))

  2. Tyler Prete Says:

    Interesting. Working my way through SICP right now, but haven’t yet covered closures (I assume it covers them, though I’m not actually sure.) Oh, by the way, to get your Lisp code to format properly, look into WP-Syntax. You can see what it does with scheme in my posts, and it works on 40 or so languages.

  3. Ramon Leon Says:

    or not… works on my blog.

  4. paul graham Says:

    I don’t think you need the block or return, and the cond should be a case.

  5. lispy Says:

    Ah. Thanks for the tip. Much more concise, now:

    (defun create-obi ()
    (let ((h (make-hash-table)))
    (lambda (command field &optional value)
    (case command
    (get (gethash field h))
    (set (setf (gethash field h) value))
    (run (apply (gethash field h) value))))))

    That brings down the part of the code doing the work to just 7 lines of code. Paul Graham’s initial proto-type (p 270 ANSI Common Lisp) was 8… but his example could handle inheritance.

  6. Top Posts « WordPress.com Says:

    […] Closures + Hash Tables = As Much OOP as You’ll Ever Need “In another thirty years people will laugh at anyone who tries to invent a language without closures, just as […] […]

  7. glardo Says:

    How silly.

    Does anyone seriously believe that, thirty years hence, we’ll be programming computers as we do now? And that discussions of closures will even exist?

  8. lispy Says:

    How lasts about five years, but why is forever.

  9. Truls Becken Says:

    Maybe I’m just stupid, but could somebody please explain the reason for the closure in this obi architecture?

    Why not just make the functions look like;

    (defun ~> (obi field value)
    (setf (gethash field obi) value))

    etc, and use hash tables as objects directly?

    Also, the ~! part doesn’t work as methods, because the stored functions have no way to access the obi they are supposed to be methods on. This would be different, however, if that part of the case expression added the obi as first argument similar to this;

    (run (apply (gethash field h) (cons h value)))

  10. lispy Says:

    Truls,

    You are correct in that the closure is being upstaged somewhat by the more run-of-the-mill feature of first-class functions in general. To really show off what can be done with closures would require a better example. But the main purpose of the closure here is to force all use of the hash table to flow through the same interface. (I realize, though, that Lisp and Perl programmers might resent a language getting in the way, but hiding implementation details like that is the first thing you have to do to be doing “object-based” programming.)

    Good point on the problem with the methods. The user should not have full access to the hash table… but methods should. Of course, if you let a user add a new method to an existing “obi” whenever he wants, then you haven’t gotten very far. So for this sort of example to be even close to being useful, we’ll have to make sure we’re implementing classes, instances, public and private fields, and so forth.

    … back to the drawing board!

  11. Truls Becken Says:

    I’ll have to admit that I love OOP, but I’m not such a big fan of complicated object models which require public, private, protected, instance, class, meta-class, meta-meta-class, meta-meta-meta… When it comes to encapsulation, the very foundation is that all state must be accessible only from inside the object’s methods. It doesn’t have to be more complicated than that.

    I see that you wanted to demonstrate encapsulation through closures, and that’s fine. I’m not so sure it’s suitable for implementing full object orientation, though. If you wanted to use closures for encapsulation and have full access to private state, I guess you would have a constructor (say make-vehicle) which declared the instance variables and somehow returned a set of closures (methods), perhaps in a hash-table. The function make-vehicle thus is the full description of the “class”. I see this as the “pure” way to do encapsulation with closures.

    The problem with this approach, however, is that 1) each “object” must store its own version of every method and 2) you’re throwing away lots of dynamism because if you add or alter a method, it will only affect objects created later on. On top of that, you’re missing inheritance, which is an important aspects of OO.

    Show me a simple way that works better than what I just described, and maybe I’ll be convinced that closures + hash tables = real OOP.

  12. lispy Says:

    I think that by using recursion to implement a form of inheritance, we can get most of the benefits of instancing and subclassing without going overboard. I do want to keep the ability to add/alter method’s on the fly without giving the user full access to the hash table. I’ll experiment with this in my spare time for a few days and then post my results.

  13. Closures + Lambda = The key to OOP in Lisp « Learning Lisp Says:

    […] + Lambda = The key to OOP in Lisp As Truls Becken remarked in a comment on a previous post, “when it comes to encapsulation, the very foundation is that all state must be accessible […]

  14. rcoder Says:

    While a multi-paradigm language like Common Lisp does indeed make it relatively easy to roll your own object system, that sort of DIY construction of language features is exactly the kind of thing that discourages, rather than encourages, reuse between systems.

    Look at the Scheme world, where there are countless slightly-incompatible implementations of objects atop lambda and alists or hash tables. If you are a library author, which object system should you pick?

    (Of course, in Common Lisp, you have CLOS, but that’s a bit more complex than closures-stored-in-tables, no?)

    A language (or at least framework) that has a single supported object *abstraction* — as opposed to implementation, which I think should be as flexible as possible — allows lightweight composition of disparate systems with much less work.

    Personally, I’m also somewhat more of a fan of message-passing (rather than direct method invocation) semantics, as I think they encourage development in a more agile style, but that’s entirely a point of personal preference.

  15. Renato Lucindo Says:

    Nice post!
    You can display the lisp code on wordpress using de code genetated by M-x htmlize-buffer. Set the variable htmlize-output-type to ‘inline-css in your .emacs to get all html necessary inside pre tags.

  16. Dru Nelson Says:

    Dude – you nailed it. Great post.

  17. gwenhwyfaer Says:

    glardo, most people aren’t even programming computers today in the way that McCarthy et al were fifty years ago… how else can we explain that whilst batch-mode OSes have gone the way of the dodo, batch-mode programming languages are still being proclaimed as cutting edge?

  18. Closures, Objects, bulldozers, and the quest for truth « Learning Lisp Says:

    […] Objects, bulldozers, and the quest for truth My first post on Closures has so far netted 2,235 hits. My second one took in 1,346 while my CodeMunger post has picked up a […]

  19. “Improve Code Configurability” Afterbirth « Learning Lisp Says:

    […] more experienced lisper to come in and set the record straight. Even Paul Graham had to drop in to correct my code! I was clearly out of my depth back then, but I at least learned in the process that a good blog […]

  20. halfdan Says:

    Great article, even as a beginner I was able to follow the line.

  21. ken Says:

    Book tip: read AMOP. Anybody thinking about object systems in Lisp owes it to themself to read this. Alan Kay was right: it was the most important book of the decade. AMOP does for object systems what SICP does for function calls.

    How to explain it in a couple sentences? Hmm… The brilliance of Lisp is that it’s written in Lisp. The brilliance of CLOS is that it’s written in CLOS. (And it’s not really that complex.) An object system written as a single hash table feels like trying to say “Fortran + closures = Common Lisp!”. 🙂

  22. Mikael Jansson » Blog Archive » C++ Coder’s Newbie Guide to Lisp-style OO Says:

    […] introduced closures over function parameters and let-bound variables and having been inspired by Closures + Hash Tables = As Much OOP as You’ll Ever Need, let’s make a leap by introducing hash tables for slot access and see what […]

  23. Advanced Codemunging: A Report from the Trenches « Learning Lisp Says:

    […] and doing all of this pretty much in public would ensure those weaknesses would become notorious among the illuminated. I wasted some time blogging about programming practices and things gradually […]

  24. An Object in Lisp. Part 1 « (defun ugly-lisp-code? () ()) Says:

    […] Looking at make-counter we see that we returned a lambda expression. Let us expand on this idea of utilizing a lambda expression for incrementing a counter and also returning it’s state ref [1]. […]

Leave a comment