Of closures, methods, procs, scope, and Ruby
by Josh Staiger
One thing I hate about Ruby is that Ruby methods aren't closures.
For instance, this blows up:
hello_string = "Hello, world!\n" def say_hello puts hello_string # bzzt! hello_string isn't in scope end say_hello # bzzt!
Which is a little odd because Ruby does support closures on procs:
hello_string = "Hello, world!\n" say_hello = Proc.new do puts hello_string end say_hello.call # Hello, world!
It kinda sucks that every time I want to create a closure I'm forced to use the less succinct ".call" syntax.
So why can procs be closures while methods cannot?
After a bit of thinking I realized this comes down to a design tradeoff on whether or not we have to type "self" or "this" all over the place when writing methods as we do in Python and JavaScript.
In Ruby we don't have to do this, and Ruby accomplishes this by making procs and methods two different things with different scoping rules.
Proc bodies have a lexical scope like functions in Lisp and JavaScript, meaning that when Ruby encounters a free variable inside a proc body, its value is resolved within the context the proc was defined. This is what makes closures possible.
Methods are different, however. A method's context is the object to which they are bound. When Ruby encounters a free variable inside a method body, it assumes the variable refers to another method on the object, and this is what saves us from having to prefix same-object methods with "this" or "self".
To demonstrate:
irb(main):171:0> hello_string = "Hello, world!" => "Hello, world!" irb(main):172:0> def say_hello irb(main):173:1> puts hello_string irb(main):174:1> end => nil irb(main):175:0> say_hello NameError: undefined local variable or method `hello_string' for # from (irb):173:in `say_hello' from (irb):175 from :0 irb(main):176:0> def hello_string irb(main):177:1> "Hello, world!" irb(main):178:1> end => nil irb(main):179:0> say_hello Hello, world! => nil0x359e4>
Personally, I'm not sure I like this tradeoff.
Procs and methods are conceptually very similar. They are both composed of a list of arguments, a block of code, and a context in which the block is evaluated. I'm not sure they should be different things.
The way I see it, a method should just be a special case of proc that happens to have an implicit "self" parameter. In this case methods would inherit the proc scoping rules and therefore could be used as closures.
If this means we have to be verbose and type "self" to call other methods on the same object, I'm fine with that.
And in fact, this is how things work in JavaScript:
var helloString = "Hello"; var myObject = { friend: "Kitty", sayHello: function() { print(helloString + " " + this.friend); } }; myObject.sayHello(); // Hello Kitty
Matz clearly recognizes that there are issues with Ruby's current scoping implementation, so maybe this is one of them.
But, hey, this kind of tradeoff may just be a Rubyish thing to do, and I probably dig closures more than most.
I haven't tried it, but I have a hunch that Ruby's flexible enough to let you make your own "def" method, passing in a block (proc) with lexical scope. Something like:
hello_string = "Hello, World!\n"
lexdef :say_hello do
puts hello_string
end
say_hello
> Hello, World!
What is great about Ruby (and unique in it's class) is that the syntax for this custom definition is strikingly similar to the built-in construct. It'd be interesting to see the issues you'd run in to while trying to support everything that a standard method call supports: variable argument lists, keyword arguments, block passing, etc.
You can, in fact, write a define_method method that takes a block and creates a method. See http://ola-bini.blogspot.com/2006/09/ruby-singleton-class.html and http://qa.poignantguide.net/chapter-6.html#section3 .
Thanks for the pointer, Bryce!
I discovered define_method shortly after writing this post and plan to write another blog entry on the topic tomorrow :)