Sunday, March 16, 2008

Meta-Variable Interpolation Cleaner In Perl Than In Ruby

For the most part, I'm a Ruby partisan. But this really sinks my battleship:

Look how much cleaner it is in Perl:

And that's with extra code added in to populate the variables so you can run it and see what it does.

I'm going to have to fix this some day.

Update: solutions from Nicolás Sanguinetti:

and Assaf Arkin:

Both these solutions are cleaner than my Ruby, but I think neither one is as clean as the Perl.

(And neither one addresses the more challenging Hpricot use case.)

This local_variables thing is one shiny new toy, though. I never saw that before.

The other interesting discovery: Nicolás says PHP has this syntax too, and calls it "variable variables." Apparently in my language snobbery I underestimated PHP. Nicolás also said that he'd been up for 26 hours on too much coffee. Dude, Nicolás, get some sleep!

Update: Dan Yoder's solutions:

puts "<img #{%w( src width height ).map { |attr| "#{attr}='#{eval attr}'}.join(' ') }/>"

assuming, as you do in the Perl example, that the attributes are already locals, since it isn't really fair to lump the hpricot interface into this. Even if we include Hpricot tho:

puts "<img #{%w( src width height ).map { |attr| "#{ attr }='#{[attr] }'}.join(' ') }/>"

it's been awhile since i hacked in perl but i don't recall being able to do things like this in one line. :-)

But again I think the Perl remains cleaner. I'm not saying it can't be done a variety of ways in Ruby - I'm just saying the Perl solution is nicer-looking than any solution I've seen in Ruby.

Update: Dan's response:

Consider, however, that it only works for variables, whereas the Ruby syntax works for any expression. Should Ruby have a separate interpolation operator for locals, like, say '$$'? Or maybe keep the interpolation as it is an introduce an operator that says "eval this":

"#{attr} = '#{$attr}"

Ah, but wait! We can actually define such a thing if we want ...

class String
  def ~ ; eval self ; end

"#{attr} = '#{~attr}"

and I am still free to use an expression here to get the value of attr if I want. I think Ruby gets it right just because it provides a single powerful interpolation operator inside of which we can do whatever we want. :-)

In fact, that is why I can write the whole loop in a single LOC. It's all inside the quotes!

No way around it - that kicks ass. The Perl really remains cleaner even still, but it does so by a much, much slimmer margin than before, and the payoff for that slim margin is a lot of power.