This post originated from an RSS feed registered with Ruby Buzz
by Christopher Cyll.
Original Post: Some consistency, please?
Feed Title: Topher Cyll
Feed URL: http://feeds.feedburner.com/cyll
Feed Description: I'm not too worried about it. Ruby and programming languages.
So I posted a few days ago about Ruby 1.9's Symbol#to_proc
addition. That cute little bit of source code that allows us to write:
burgers.map(&:sauce)
Pretty cool, eh? Only it turns out there's a problem. But first why
don't we talk a little bit about Procs and Lambdas in Ruby?
I mention this briefly in my article If
It's Not Nailed Down, Steal It, but there are some differences
between them.
Of course, it's not really accurate to talk about "Lambdas." Lambda
expressions still return instances of the Proc class.
lambda{|x| x }.class.name ===> Proc
But Procs created with "Proc.new" and "lambda" do behave
differently. First of all, a "return" statement inside a
lambda-created Proc will return control from the block. A "return"
statement inside a Proc.new-created Proc will return from the method
where the Proc was defined.
This difference is weird, but fairly utilitarian. After all, it's
fairly common to use an each iterator statement inside a method and
want to terminate both the iteration and the method early with a
return statement. If return only exited the Proc/block, you wouldn't
have a good way to short circuit out. Of course as it as, it's not
really a general solution for arbitrary levels of nesting, but that's
a sort of larger problem (perhaps solved by naming your blocks?
hmmm...)
Anyways, but then it's also nice to have lambdas behave like methods
for those situations when you need that kind of behavior. So for
better or worse, that's one difference.
So, what else is different and what does this have to do with the
Symbol#to_proc problem I alluded to earlier? Well, have a look at this
code and its output:
Yikes! What's going on here?
Well, it turns out that Procs and lambdas also have different
parameter destructuring rules. I talk about destructuring in If
It's Not Nailed Down..., but the short version is that it's a way
to automatically unpack elements out of a data structure. We do this
all the time in Ruby with:
a, b = [1, 2]
a
==> 1
b
==> 2
You see, Procs/blocks automatically unpack their arguments in some
situations. You may have even used this to your benefit. Anytime
you're working with groups of elements (pairs, for example), it's nice
to be flexible about whether the person you're passing the group to
wants the group or the separate items. So these two lines are the
same:
So, in conclusion, by using Proc.new in the definition of
Symbol#to_proc, some situations were introduced where the loosy-goosey
handling of parameters in Procs/blocks caused problems. The problem
was easily fixed, however, by replace Proc.new with lambda.
Update: Oops. In all my excitement over the dfferences between
Proc.new and lambda, I complete forgot that the problem was actually
fixed by forcing an array glom of the arguments and using the first
one as invocant. This makes the final implementation:
class Symbol
def to_proc
Proc.new{|*args| args.shift.__send__(self, *args)}
end
end
Maybe there are some other differences between Proc and Lambda that
make this approach more suitable? Anyways, pretty cool. Read Rodney's
post for more info.
End update
The one other final change that ended up happening was to use
.__send__ instead of .send because some classes (like Socket) redefine
send. Doh. Anyways, good they fixed it, but shame on Socket (although
you can understand why they'd want to use the method name send).
A
very informative post by Rodney of pinupgeek.com put this on my
radar, and you can read more on the Ruby Talk mailing list here
and here.