The Artima Developer Community
Sponsored Link

Ruby Buzz Forum
Some consistency, please?

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Christopher Cyll

Posts: 49
Nickname: topher
Registered: Jan, 2006

Topher Cyll is Rubyist and writer in Portland, Oregon.
Some consistency, please? Posted: Jun 22, 2006 3:24 PM
Reply to this message Reply

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.
Latest Ruby Buzz Posts
Latest Ruby Buzz Posts by Christopher Cyll
Latest Posts From Topher Cyll

Advertisement
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:

require 'pp'
lambda{|a, *b| pp [a, b]}.call([1, 2, 3])
  ==> [[1, 2, 3], []]
Proc.new{|a, *b| pp [a, b]}.call([1, 2, 3])
  ==> [1, [2, 3]]

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:

Proc.new{|a, b| pp [a, b] }.call(1, 2)
 ==> [1, 2]
Proc.new{|a, b| pp [a, b] }.call([1, 2])
 ==> [1, 2]

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.

Read: Some consistency, please?

Topic: Unleash the Tiger Previous Topic   Next Topic Topic: RailsConf 2006 is only 2 days away

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use