ruby defined? # => “expression”

Ever spend a long time debugging something, only to find out an hour or two later that you were staring at the problem all along, and you should have realized it from the beginning? And then, of course, the solution only requires a tiny tweak to one line, perhaps merely the addition of a few parenthesis.

>> defined? RAILS_ENV
=> "constant"
>> defined? RAILS_ENV && RAILS_ENV == "development"
=> "expression"
>> (defined? RAILS_ENV) && (RAILS_ENV == "development")
=> false

D’oh! (let’s ignore for now the issue of whether or not the “fixed” code is reasonable to run anyway)

I was curious to see if the expression was evaluated, and it appears that it is… sort of:

>> def foo
>>   $stderr.puts ":foo"
>> end
=> nil
>> def bar
>>   $stderr.puts ":bar"
>>   raise "hell"
>> end
=> nil
>> defined? foo
=> "method"
>> defined? bar
=> "method"
>> defined? foo || 1
:foo
=> "expression"
:bar
>> defined? bar || 1
=> nil

Notice: the methods were executed, but the exception from bar was caught and squirreled away somewhere, and defined? the returned nil, even though the method and expression were certainly “defined”.

Tags:

4 Responses to “ruby defined? # => “expression””

  1. Avdi says:

    This is some great exposition on a corner of Ruby I knew little about. Thanks!

  2. Brandon says:

    can you explain why this is the case?

  3. nick says:

    @brandon: Why is what the case?

    Why is the entire expression passed to defined?, rather than just the first arg? because defined? isn’t a method call but a keyword, and it has very low precedence. defined? CONST and CONST == "foo" would have given the expected result, even without parentheses, because of the lower precedence of and.

    Why is the expression evaluated? I dunno; Principle of Least Surprise, I guess. I’d need to ask Matz (or ruby-talk) to be certain. The weird thing is that pg 113 of “The Ruby Programming Language” states that “the expression that is the operand to defined? is not actually evaluated; it is simply checked to see whether it could be evaluated without error.” But that seems to contradict by my irb session, pasted in the post. The methods were clearly evaluated (allowing the printing and raising exception side effects).

    Initially, I was surprised that the exception gets eaten. But I supposed that’s to be expected as well: Calling defined? will never throw an exception, but will always return a truthy (string) or falsy (nil) value.

    Does that help?

  4. Ian says:

    The issue is the `&&` and `and` have different levels of precedence. I use `defined?(ARG)` with parentheses on a regular basis to avoid this issue.

Leave a Reply