Coercion operations affect which order you might evaluate variables in

In trying to write code to give me the full name of “next” month (in my case, November, as today is October 6), I just stumbled across this interesting little Rubyism. Indeed, as you’ll see below, I’m simply trying to add one thing to another thing. In other languages and what we were taught in math, the order of these two things don’t matter. You’d think a + b is the same as b + a. In general it is, but here’s an interesting edge case involving manipulating dates.

Take our a + b example above. As an object-based language Ruby is going to call the plus method on a object, passing it the parameter b. In doing so, it will try to coerce that parameter to something which can be added (as in the plus method).

1.month returns a number, or FixNum more precisely, which is normally happy to be cast (coerced) into a Time object.

But a Time can’t be cast as a Fixnum (why I don’t know and probably shouldn’t ask). So you get behavior like this:

>> (1.month + Time.now).strftime(“%B”)
TypeError: Time can’t be coerced into Fixnum
from /some/path/to/my/project/vendor/rails/activesupport/lib/active_support/duration.rb:20:in `+’
from /some/path/to/my/project/vendor/rails/activesupport/lib/active_support/duration.rb:20:in `+’
from (irb):14

Time.now is a time object, and the plus method on the fixnum that evaluates from 1.month doesn’t know what to do with a time object.

But, switch to b + a, and it works:

>> (Time.now + 1.month).strftime(“%B”)
=> “November”

This is because the time object’s plus method knows what to do with a fixnum – treat it as the number of seconds to add to the time.

For completion sake, let’s make sure everything I said up top is actually correct…

?> Time.now.class
=> Time
>> 1.month.class
=> Fixnum
>> (Time.now + 1.month).class
=> Time

Leave a Reply





Your email address will not be published. Required fields are marked *

Previous post Configure git so that it shows green & red in my terminal
Next post NY Tech Meetup Oct 6, 2009: Tagnic, Anyclip