Igor P. wrote in post #1082740:
You are misleading us here with {{ def bar; “Method bar”; end }} before
the assignment in {{ bar if bar = 123 }}, because parser now thinks that
it sees the method bar.
On the contrary, it is you who are misleading people with your
inaccurate information.
In your defence, I will say that ruby is a language which is not
formally specified or documented, and therefore it is easy to come up
with misinterpretations of what you see. However, there are people on
this list who will try to enlighten you, if you will take the trouble to
listen.
Try it with a new variable, that is not shadowed
by another token like in your case the bar method, and you will see,
that interpreter exits with the error! For instance:
p “v5:#{v5 if ((v5=‘created’)!=’’)}” #=> FAILS with:
# t.rb:28:in <main>': undefined local variable \ # or method
v5’ for main:Object (NameError)
Indeed it does, and the error message says “undefined local variable or
method”
Ruby has interpreted the first v5 as a method call, because no local
variable assignment statement exists lexically earlier in the scope. So
it has tried to call method v5(), and failed.
However it cannot know whether the programmer intended v5 to be a
variable or method. It could be that you intended to use a variable, but
mistyped the name:
v5 = nil
puts vv5 # Oops, I mistyped the variable name
vv6 = nil # Oops, I mistyped the variable name at assignment time
puts v6
Or it could be that you intended to call a method but mistyped it:
def v7; “ok”; end
puts vv7 # Oops, I mistyped the method name
So the best that Ruby can say is “undefined local variable or method”.
But what it actually means is: v5 is definitely not a local variable
at this point, so I tried invoking it as a method, but there is no
method called v5 at this point in time.
Note: whether v5 exists as a method or not can only be known at runtime,
because methods are created dynamically as the program runs.
Precedence may not be important, though I am not sure it is not, since
the run-time must wait for the condition to be evaluated, therefore it
will execute the assignment inside the ‘if’ expression in accordance
with precedence, namely prior ‘if’ expression itself is evaluated, hence
allowing the evaluation of the expression in front of the conditional,
the run-time was waiting for.
Wrong. The resolution of the variable name / method call ambiguity is
not done at run-time. It is done at parse time, when the source file
is being read in and the AST is being built, before the AST is executed.
If you don’t believe me, then try making a source file which cannot be
parsed, like this:
puts “hello”
class Foo # missing end
You will see something like this:
syntax error, unexpected $end
But notice that the word “hello” does not appear on your screen. This is
because execution cannot start until the file has been completely parsed
from top to bottom. Balancing class/end and def/end is a parse-time
activity.
Now, it is during this parsing phase that the decision is made as to
whether a bareword ‘foo’ is to be interpreted as a method call or as a
local variable reference. And this happens before even one statement
from this file has been executed.
Also, the fact that {{ puts var||=‘n/a’ if var=123 }} works as expected,
and {{ puts var if var=123 }} does not, proves that evaluation here is
right-to-left regardless of the fact that parser works in the opposite
direction, after all, if parser would react to the first assignment it
encountered in ‘strictly’ left-to-right fashion the result should be
‘n/a’ and not 123 that was evaluated last.
var||=‘n/a’ is an assignment to var and therefore springs a local
variable into existence at that point lexically. You can consider it as
a shortcut for var = var || ‘n/a’, although it is not exactly like that.
The execution sequence obviously requires the ‘if’ condition to be
evaluated before the LHS can be conditionally executed.
if var = 123 # assigns 123 to var, evaluates as true, hence LHS is
run
puts var||=‘n/a’ # prints 123, because var has already been assigned
to
So evaluation is right-before-left, at the time where the AST is
executed. This still does not change the fact that it is parsed
left-to-right.
Hence, if you have not assigned to var previously, then
puts var if var=123
is parsed as
puts var() if var=123
It treats the first bareword ‘var’ to be resolved as a method call; it
will fail unless a method called ‘var’ has been defined.
The final point you need to be clear on: whether an assignment is
executed or not makes no difference. It’s simply whether it is parsed
as one. So for example:
if false
baz = 123
end
puts baz # prints nil - does not give an error
Regards,
Brian.