Of closures, methods, procs, scope, and Ruby


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
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
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.