Zen and the art of statefulness
The venerable master Qc Na was walking with his student, Anton. Hoping to prompt the master into a discussion, Anton said “Master, I have heard that objects are a very good thing - is this true?” Qc Na looked pityingly at his student and replied, “Foolish pupil - objects are merely a poor man’s closures.”
Chastised, Anton took his leave from his master and returned to his cell, intent on studying closures. He carefully read the entire “Lambda: The Ultimate…” series of papers and its cousins, and implemented a small Scheme interpreter with a closure-based object system. He learned much, and looked forward to informing his master of his progress.
On his next walk with Qc Na, Anton attempted to impress his master by saying “Master, I have diligently studied the matter, and now understand that objects are truly a poor man’s closures.” Qc Na responded by hitting Anton with his stick, saying “When will you learn? Closures are a poor man’s object.” At that moment, Anton became enlightened.
When I first read the above koan some time ago, I didn’t really understand it. I had a very basic idea of closures, but at the time they were just a syntactic oddity to me - something you could do a few cool things with, but not particularly useful. Since then I’ve worked through quite a bit of Structure and Interpretation of Computer Programs and delved into functional programming in JavaScript, which has given me a much deeper understanding of closures. On the other side of the divide, I’ve been doing a lot of Ruby programming, which has helped me grok objects a lot better. I now feel like I can begin to comprehend what the quote was getting at.
My moment of enlightenment came today while reading Test-Driven JavaScript Development and looking at the code for a JavaScript strftime
function (abbreviated here for brevity):
Date.prototype.strftime = (function () {
function strftime(format) {
var date = this;
return (format + "").replace(/%([a-zA-Z])/g, function (m, f) {
//format date based on Date.formats
});
}
// Internal helper
function zeroPad(num) {
return (+num < 10 ? "0" : "") + num;
}
Date.formats = {
// Formatting methods
d: function (date) {
return zeroPad(date.getDate());
},
//...
//various other format methods
//...
// Format shorthands
F: "%Y-%m-%d",
D: "%m/%d/%y"
};
return strftime;
}());
The above code uses an IIFE (Immediately-invoked function expression) to produce a function with additional data. If it doesn’t make sense, I thoroughly recommend Ben Alman’s post on IFFEs for an overview of the technique. The code executes, and returns a function. The important thing is that one function is used to define another function and its context.
In Ruby, when you define a class, the class definition is executed as Ruby code, unlike in Java, for example, where a class definition is just a syntactic construct read at compile-time, but not executed in the way other Java code is. A Ruby class definition is read at run-time, and builds up a new class as it is interpreted.
In a lot of ways, Ruby class definitions and JavaScript function-defining functions are equivalent. I’ll give you a little example to illustrate:
zen.js
var Cat = function () {
var age = 1;
function catYears() {
return age * 7;
}
function birthday() {
age++;
}
return {
catYears: catYears,
birthday: birthday
}
};
var powpow = Cat();
var shorty = Cat();
//yo shorty, it's your birthday:
shorty.birthday();
alert(powpow.catYears()); // => 7
alert(shorty.catYears()); // => 14
zen.rb
class Cat
def initialize
@age = 1
end
def cat_years
@age * 7
end
def birthday
@age += 1
end
end
powpow = Cat.new
shorty = Cat.new
#yo shorty, it's your birthday:
shorty.birthday
puts powpow.cat_years # => 7
puts shorty.cat_years # => 14
Strictly speaking, in zen.js
I’m returning an object, but the private data and methods of that object are saved in a closure. So, zen.js
stores its state in a closure, while zen.rb
stores its state in an object. Every time Cat()
is called in zen.js
, a new closure is created with a unique age
. Every time Cat.new
is called in zen.rb
, a new object is created with a unique @age
. (These two examples aren’t strictly equivalent - each cat in zen.js
gets a new copy of the functions, whereas in zen.rb
they share the same methods. It’s possible to make the JavaScript version function more like a Ruby class definition, but it takes a bit more code.)
Of course, there’s a lot you can do with JavaScript closures that you can’t do with Ruby objects. And there’s a lot you can do with Ruby objects that can’t be emulated using JavaScript closures. Any time you decide that one is better than the other, just imagine Qc Na hitting you with his stick.
Further reading
- SICP – More closures than you can shake a zen stick at (although they use the word “closure” to describe a different concept)
- What’s so cool about Scheme? – The source of the original koan and an interesting discussion of closure-object equivalence
- Closures And Objects Are Equivalent – In depth discussion of the topic on Ward Cunningham’s WikiWikiWeb
- OO in FP – Another discussion of objects in functional programming