Why be evil.rb?

Caius Durling [shared some “hax”][hax] with the Ruby community that inspired some [discussion over at Hacker News][hn]. I commented there, but it seemed like a decent topic for a blog post. One of the examples illustrates a means to define a method-local variable in the argument definition list:

def output name=((default=true); “caius”)
“name: #{name.inspect} — default: #{default.inspect}”

output() # => “name: \”caius\” — default: true”
output(“fred”) # => “name: \”fred\” — default: nil”

Have a closer look at the method definition line. This code works because of the way parenthesis are handled. Much like in math, when parenthesis are encountered, we evaluate from the inside out. Fire up an IRB session and run this code:

((default=true); “caius”)

The return value given for the first line is “caius”, which gets assigned to “name” in our argument list, but you can also see that default is set to “true”. Using the semi-colon statement separator only works because we’ve wrapped the whole thing in parenthesis. That’s why the return value is “caius”. We leverage Ruby’s last-line return value feature.

This might all seem trivial to you if you’ve been programming in Ruby for a while, but therein lies the crux of the discussion. If we only ever wrote code for ourselves, this would be a non-argument. If we expect other people to read and use our code, we should write in a way that is easy to interpret.

Like many viewpoints, the “wrongness” of this example is not black & white; it’s shades of gray. On one hand, you have the “anything that will eval is valid Ruby” view, and on the other you have the “If it’s not immediately obvious to a beginner, you shouldn’t do it” view. There may be better ways to express those two sides of the matter, but that’s the general idea.

The problem with this code (from the latter viewpoint) is that it crams too much program logic in to the argument definitions. This example uses parenthesis to force the evaluation of `default=true; “caius”` in the argument definition list. That’s only two statements, but it violates some common expectations:

1. We generally expect argument definitions to be clear and readable, so that method definitions are self documenting (to some degree); this approach clutters the argument definitions.

2. We expect argument definitions to sometimes assign default values.

3. We expect program logic to appear in the body of a method, or to be DRY’d up in separate methods.

In this way, the example is not “incorrect” but awkward. To borrow an idea from the literate programming camp, I’d say that just because you can write awkward sentences with valid grammar, it doesn’t mean you should.

Side note: In Caius’ defense, he does say never to use these.