Ruby conditionals subtlety?

Hi,

Anyone know why thse two forms of "unless" behave differently?

irb

irb(main):001:0> foo = true unless defined?(foo)
=> nil
irb(main):002:0> unless defined?(fooo) ; fooo = true ; end
=> true

thx

···

--
Posted via http://www.ruby-forum.com/\.

Hi,

Anyone know why thse two forms of "unless" behave differently?

> irb
irb(main):001:0> foo = true unless defined?(foo)
=> nil
irb(main):002:0> unless defined?(fooo) ; fooo = true ; end
=> true

thx
--
Posted via http://www.ruby-forum.com/\.

From playing around it doesn't look like unless is "behaving differently"
Ruby has created a label for foo since it thinks it is going to be assigning
it something (dynamic language)

For example

foo = true unless defined?(foo)

=> nil

unless defined?(fooo); fooo = true; end

=> true

foo

=> nil

fooo

=> true

foo = true unless defined?(foom)

=> true

foo

=> true

Basically it looks like foo appears to exist but it doesn't really exist yet
so the first unless "acts" oddly. This is very interesting though. Perhaps
someone who knows what they are talking about can tell us why it appears to
behave this way.

···

On Thu, Feb 18, 2010 at 12:49 PM, Farhad Farzaneh <ff@onebeat.com> wrote:

--
"Hey brother Christian with your high and mighty errand, Your actions speak
so loud, I can’t hear a word you’re saying."

-Greg Graffin (Bad Religion)

oddity in the way the code is parsed:

% echo "foo = true unless defined?(foo)" | parse_tree_show
s(:if, s(:defined, s(:lvar, :foo)), nil, s(:lasgn, :foo, s(:true)))

% echo "unless defined?(fooo) ; fooo = true ; end" | parse_tree_show
s(:if, s(:defined, s(:call, nil, :fooo, s(:arglist))), nil, s(:lasgn, :fooo, s(:true)))

···

On Feb 18, 2010, at 11:49 , Farhad Farzaneh wrote:

Hi,

Anyone know why thse two forms of "unless" behave differently?

irb

irb(main):001:0> foo = true unless defined?(foo)
=> nil
irb(main):002:0> unless defined?(fooo) ; fooo = true ; end
=> true

Ryan Davis wrote:

···

On Feb 18, 2010, at 11:49 , Farhad Farzaneh wrote:

Hi,

Anyone know why thse two forms of "unless" behave differently?

irb

irb(main):001:0> foo = true unless defined?(foo)
=> nil
irb(main):002:0> unless defined?(fooo) ; fooo = true ; end
=> true

oddity in the way the code is parsed:

% echo "foo = true unless defined?(foo)" | parse_tree_show
s(:if, s(:defined, s(:lvar, :foo)), nil, s(:lasgn, :foo, s(:true)))

% echo "unless defined?(fooo) ; fooo = true ; end" | parse_tree_show
s(:if, s(:defined, s(:call, nil, :fooo, s(:arglist))), nil, s(:lasgn,
:fooo, s(:true)))

Cool, any chance you could give a short description for those of us that
have never really thought about the parser or used parse_tree_show?

thx

--
Posted via http://www.ruby-forum.com/\.

Your latter code snippet treats "fooo" in defined? as a method call. This is because the assignment inside the conditional hasn't been parsed yet, and hasn't affected the lookup tables.

The former doesn't have this problem because the body of the conditional is parsed first.

···

On Feb 18, 2010, at 13:44 , Farhad Farzaneh wrote:

Ryan Davis wrote:

On Feb 18, 2010, at 11:49 , Farhad Farzaneh wrote:

Hi,

Anyone know why thse two forms of "unless" behave differently?

irb

irb(main):001:0> foo = true unless defined?(foo)
=> nil
irb(main):002:0> unless defined?(fooo) ; fooo = true ; end
=> true

oddity in the way the code is parsed:

% echo "foo = true unless defined?(foo)" | parse_tree_show
s(:if, s(:defined, s(:lvar, :foo)), nil, s(:lasgn, :foo, s(:true)))

% echo "unless defined?(fooo) ; fooo = true ; end" | parse_tree_show
s(:if, s(:defined, s(:call, nil, :fooo, s(:arglist))), nil, s(:lasgn,
:fooo, s(:true)))

Cool, any chance you could give a short description for those of us that
have never really thought about the parser or used parse_tree_show?

Ryan Davis wrote:

irb(main):002:0> unless defined?(fooo) ; fooo = true ; end

Cool, any chance you could give a short description for those of us that
have never really thought about the parser or used parse_tree_show?

Your latter code snippet treats "fooo" in defined? as a method call.
This is because the assignment inside the conditional hasn't been parsed
yet, and hasn't affected the lookup tables.

The former doesn't have this problem because the body of the conditional
is parsed first.

Thanks. To make sure I understand: In the first case,

foo = true unless defined?(foo)

the parser encounters 'foo', adds it to the symbol table, and then sees
the defined? call, so the net result is that foo is defined, but has nil
value. As such, if we're ever going to use defined? as a conditional,
it should precede any other mention of the token (foo).

Is this correct?

···

On Feb 18, 2010, at 13:44 , Farhad Farzaneh wrote:

--
Posted via http://www.ruby-forum.com/\.

No, it isn't necessarily defined. AT PARSE TIME it sees the initial foo and decides it is a local variable so it treats the foo in defined?(foo) as a local variable. AT RUN TIME it evaluates the defined?(foo) and acts accordingly.

···

On Feb 18, 2010, at 16:04 , Farhad Farzaneh wrote:

Ryan Davis wrote:

On Feb 18, 2010, at 13:44 , Farhad Farzaneh wrote:

irb(main):002:0> unless defined?(fooo) ; fooo = true ; end

Cool, any chance you could give a short description for those of us that
have never really thought about the parser or used parse_tree_show?

Your latter code snippet treats "fooo" in defined? as a method call.
This is because the assignment inside the conditional hasn't been parsed
yet, and hasn't affected the lookup tables.

The former doesn't have this problem because the body of the conditional
is parsed first.

Thanks. To make sure I understand: In the first case,

foo = true unless defined?(foo)

the parser encounters 'foo', adds it to the symbol table, and then sees
the defined? call, so the net result is that foo is defined, but has nil
value. As such, if we're ever going to use defined? as a conditional,
it should precede any other mention of the token (foo).

I should also point out: almost all of this is of no consequence. You almost never use defined? on a local variable like this. You usually use it on a const, ivar, cvar, or global, and all of those are unambiguous.

···

On Feb 18, 2010, at 16:04 , Farhad Farzaneh wrote:

Ryan Davis wrote:

On Feb 18, 2010, at 13:44 , Farhad Farzaneh wrote:

irb(main):002:0> unless defined?(fooo) ; fooo = true ; end

Cool, any chance you could give a short description for those of us that
have never really thought about the parser or used parse_tree_show?

Your latter code snippet treats "fooo" in defined? as a method call.
This is because the assignment inside the conditional hasn't been parsed
yet, and hasn't affected the lookup tables.

The former doesn't have this problem because the body of the conditional
is parsed first.

Thanks. To make sure I understand: In the first case,

foo = true unless defined?(foo)

the parser encounters 'foo', adds it to the symbol table, and then sees
the defined? call, so the net result is that foo is defined, but has nil
value. As such, if we're ever going to use defined? as a conditional,
it should precede any other mention of the token (foo).

Ryan Davis wrote:

···

On Feb 18, 2010, at 16:04 , Farhad Farzaneh wrote:

yet, and hasn't affected the lookup tables.

value. As such, if we're ever going to use defined? as a conditional,
it should precede any other mention of the token (foo).

I should also point out: almost all of this is of no consequence. You
almost never use defined? on a local variable like this. You usually use
it on a const, ivar, cvar, or global, and all of those are unambiguous.

Thanks. I sometimes use it in Rails partials rendering, where I may
pass an optional local variable, but if it isn't passed, I want to set
it to some default value. Perhaps there's a better way of doing this...
--
Posted via http://www.ruby-forum.com/\.

Farhad Farzaneh wrote:

I should also point out: almost all of this is of no consequence. You
almost never use defined? on a local variable like this. You usually use
it on a const, ivar, cvar, or global, and all of those are unambiguous.

Thanks. I sometimes use it in Rails partials rendering, where I may
pass an optional local variable, but if it isn't passed, I want to set
it to some default value.

BTW, I wonder,

In templates, local variables are created dynamically (e.g., the
programmer passes a hash of "variables" to the template engine). So how
can Ruby know, in the template code, that a "name" refers to a variable?
By default Ruby thinks names are method invocations and since there's no
assignment, Ruby has no way to know these are variables...

···

--
Posted via http://www.ruby-forum.com/\.

var ||= default

···

On Feb 18, 2010, at 17:48 , Farhad Farzaneh wrote:

Ryan Davis wrote:

On Feb 18, 2010, at 16:04 , Farhad Farzaneh wrote:

yet, and hasn't affected the lookup tables.

value. As such, if we're ever going to use defined? as a conditional,
it should precede any other mention of the token (foo).

I should also point out: almost all of this is of no consequence. You
almost never use defined? on a local variable like this. You usually use
it on a const, ivar, cvar, or global, and all of those are unambiguous.

Thanks. I sometimes use it in Rails partials rendering, where I may
pass an optional local variable, but if it isn't passed, I want to set
it to some default value. Perhaps there's a better way of doing this...

that's a rails question. Please take it to a rubyonrails forum (or dig up the code--but I really don't recommend that).

···

On Feb 18, 2010, at 23:06 , Albert Schlef wrote:

Farhad Farzaneh wrote:

I should also point out: almost all of this is of no consequence. You
almost never use defined? on a local variable like this. You usually use
it on a const, ivar, cvar, or global, and all of those are unambiguous.

Thanks. I sometimes use it in Rails partials rendering, where I may
pass an optional local variable, but if it isn't passed, I want to set
it to some default value.

BTW, I wonder,

In templates, local variables are created dynamically (e.g., the
programmer passes a hash of "variables" to the template engine). So how
can Ruby know, in the template code, that a "name" refers to a variable?
By default Ruby thinks names are method invocations and since there's no
assignment, Ruby has no way to know these are variables...

Maybe it's clearer like this:

  if false
    foo = 123
  end
  puts foo # nil

  puts bar # undefined local variable or method 'bar'

That is, for a bare word expression like 'foo' ruby has to decide
whether to parse it as a method call - as foo() or self.foo - or as a
local variable reference.

It makes the decision based on whether there has been a previous
assignment of the form "foo = ..." parsed earlier in the code. This is
regardless of whether the code is actually executed, because we haven't
started executing any of it yet.

"earlier" in the code is strictly left-to-right.

   foo = 123 if not defined?(foo)

At the point of defined?, "foo = ..." has already been seen, and so
bareword foo is known to be a local variable, and therefore the symbol
is 'defined' as a local variable at this point in the source code,
whether or not an assignment has actually been made.

···

--
Posted via http://www.ruby-forum.com/.

Your latter code snippet treats "fooo" in defined? as a method call. This is because the assignment inside the conditional hasn't been parsed yet, and hasn't affected the lookup tables.

The former doesn't have this problem because the body of the conditional is parsed first.

And:

···

On Thu, Feb 18, 2010 at 5:18 PM, Ryan Davis <ryand-ruby@zenspider.com> wrote:

On Sat, Feb 20, 2010 at 5:28 AM, Brian Candler <b.candler@pobox.com> wrote:

Maybe it's clearer like this:

if false
foo = 123
end
puts foo # nil

puts bar # undefined local variable or method 'bar'

That is, for a bare word expression like 'foo' ruby has to decide
whether to parse it as a method call - as foo() or self.foo - or as a
local variable reference.

It makes the decision based on whether there has been a previous
assignment of the form "foo = ..." parsed earlier in the code. This is
regardless of whether the code is actually executed, because we haven't
started executing any of it yet.

I think I understand the gist of this -- if the parser has seen foo
already, it is considered "defined" -- but how does the distinction of
method vs. local variable matter in this context? As far as I can
tell, defined? works the same on local variable names and method
names.