Tiny (but maybe important) Rubinius tidbits

Saturday, April 19, 2008
As you play with a tool you progressively discover various small tidbits about it. I have a few in mind and I thought it would helpful to share them in case you encounter them and get this weird expression on your face of "huh?".

1. The multiple assignment operator
If you are used to use it's return values you'll have to think again cause in Rubinius it doesn't return the array with the new values of the variables. So let's say that you were doing this in Ruby 1.8, Ruby 1.9, JRuby, etc... :
p((a,b = 'some value', 'and some other value'))
This would result in :
["some value", "and some other value"]

In Rubinius it's not the case:
true

Hmm... Pretty nasty if you depend on the result...

Of course, this is not something really common, but in my case I was (unnecessarily) returning an array of values from inside a method that could be later used from the calling one. As the people at #rubinius pointed out I could do it with a much more nice and elegant way. The excellent quote after my "dough! Why didn't I think this in the first place?" was:
"See? Rubinius makes you write better code!" - Nice one :D

So, keep that in mind for the time being. They are thinking to make this behave as in the rest VMs but it's not sure yet since from what I've heard it does require some additional effort. Why don't you try yourself?

In contrast, the JRuby team has an open ticket about a possible optimization where the interpreter will be able to understand whether the value is going to be used or not, thus avoiding the unnecessary Array allocation - cool!

2. The nasty Symbol#to_proc and the splat ( * ) operator.
I've given some links about this issue before but I think here is more appropriate and some more explanation would be nice.

First of all, for those who don't know what does this method do you can find a short introduction on this post from Dave Thomas and from plenty other resources (Dr Nic, Invisible Blocks and InfoQ) including a railcast episode for the more video-podcast-savvy people. Keep also in mind the possible downside of using this method as noted by Pratik Naik.

Shall we go on now? Nice.

There are various approaches on this implementation like the one from Ruby Facets, Rails and Rubinius one. There's even a different proposal that got rejected from the Rails core team. As I mentioned in the appropriate ticket in Lighthouse, the current implementation doesn't work as it should. I haven't managed to track the problem down but it seems to me that it has something to do with the splat operator. Since I'm not sure, further investigation on this weird behavior is needed. For the time being I found a temporary solution to this that works fine with my examples. For those of you who are lazy enough to go and see all these links here are some examples:

(1..100).map(&:to_s) => ["1", "2", "3", (....)
(1..100).inject(&:+) => 5050
These work just fine.
But:
[(1..10).to_a, (1..10).to_a].map(&:to_s)
ArgumentError: wrong number of arguments (got 9, required 1)

(1..100).inject(&:to_s)
ArgumentError: wrong number of arguments (got 1, required 0)

Are obviously not.

Now, my (not so pretty) fix to this problem is this:
class Symbol
def to_proc
Proc.new { |*args|
obj = args.shift
args.empty? ? obj.__send__(self) : obj.__send__(self, *args)
}
end
end
which only breaks on the last example used before:
(1..100).inject(&:to_s)
ArgumentError: wrong number of arguments (got 1, required 0)
and that's because the #inject method yields two parameters in the block (and so it makes sense for this to break I believe and that's why I didn't include a test case for something like this in the specs I wrote for the patch.).

Feel free to share your thoughts.

0 comments:

Post a Comment