Evil: knowing when a method got passed the default value

Let’s say you’re crazy. Now, as a crazy person, you might have a method like this:

def greet_world(salutation="Hello")
  "#{salutation} World!"
end

You’re not so crazy that you can’t write working Ruby code. Anyway, it’s totally useful:

greet_world          # => "Hello World!"
greet_world('Hi')    # => "Hi World!"
greet_world('Yo')    # => "Yo World!"
greet_world('Hola')  # => "Hola World!"

But then you see someone do this:

greet_world('Hello')  # => "Hello World!"

And it drives you even crazier. Being totally crazy, you feel a compulsion to make fun of people who pass the default value for an argument of a method. But how in the world can you tell?

Well, it turns out (and you probably know this already) that the argument list of a method is a proper venue for executing code. You know this because you’ve seen this a thousand times: def something(time=Time.now). That Time.now happens when you call the method without that optional argument. It doesn’t happen when Ruby first parses the method and adds it to the method table for the class. That wouldn’t be too helpful.

So… you can execute arbitrary code in the method argument list. But what kind? What’s the scope? Well, anything you can do in a method body is fair game (and anything you can’t is not). You can’t define a class, for instance. And the scope? The method body itself.

You’re crazy, remember? But not so crazy that you don’t remember that every statement has a return value. Like assignment. Assignment returns a value. And you’ve got a place in the argument list that’s scoped with the method body. And you’ve got a deep desire to mock people who pass default values.

BOOM!

def greet_world(salutation=(salutation_not_given='Hello'))
  if salutation == 'Hello' and not salutation_not_given
    "YOU HAVE NO IMAGINATION!"
  else
    "#{salutation} World!"
  end
end

greet_world('Hello')  # => "YOU HAVE NO IMAGINATION!"
greet_world           # => "Hello World!"
greet_world('Hi')     # => "Hi World!"

salutation_not_given is nil if you’ve given a value. It’s one of those things.

ANYWAY. Use that at your own peril.

4 Comments »

  1. crayz said,

    April 12, 2008 @ 9:57 pm

    Wow, just wow. That is ridiculous, but I can actually see it coming in handy sometimes (e.g. maintaining backwards compatibility with an API that’s changing default values in a method call)

    Very cool tip

  2. João Paulo Lins said,

    April 13, 2008 @ 9:16 am

    I think to do conditional within method is best, because code is more legible and safe.However is like you said: “Use that at your own peril.”

    def greet_world(salutation = nil)
    salutation = salutation.nil? ? ‘Hello’ : salutation == ‘Hello’ ? ‘YOU HAVE NO IMAGINATION!’ : salutation
    end

  3. Chris Shea said,

    April 13, 2008 @ 9:47 am

    @João: The original problem was that I had a method that should take any object whatsoever and return something based on that, but return something else if the method wasn’t passed anything. A default value of nil wouldn’t help in that case, because the method was expected to handle nil just like everything else.

    The solution I ended up with was to rethink my approach. But x=(x_not_given = true) could have worked.

  4. Ben Goering said,

    April 16, 2008 @ 5:00 pm

    What a fabulous way to humiliate fools!

    Cool post, though. Even a ruby noob like me can understand it!

RSS feed for comments on this post · TrackBack URI

Leave a Comment