Assigning an undefined variable in a one line condition [duplicate] - ruby

This question already has answers here:
Ruby if vs end of the line if behave differently?
(5 answers)
Closed 8 years ago.
In Ruby, why can you write :
# b is not defined yet.
#
if b = true
a = b
end
# => a = true
But not a one-liner :
a = b if b = true
# => NameError: undefined local variable or method `b' for main:Object

Because the Ruby interpreter "creates" a local variable when it sees an assignment.
In the second case, it hasn't yet seen the assignment, so the variable doesn't exist when the expression is parsed.
To be more precise, a method is first parsed into an internal representation, and then, perhaps, the code will eventually be called and actually executed.
Variables are "created" in that parsing pass. It's really more a matter of declaration, it just means that the interpreter becomes aware of them. They won't be created in the sense of being given space or a value until the surrounding method is called by someone.

Related

Non executed line (blocked by "if false") still affects results [duplicate]

This question already has answers here:
Ruby instance method & conditional local variable assignment with same name
(2 answers)
Closed 3 years ago.
I wanted to use the classical ||= re-assignment (cf Set Ruby variable if it is not already defined) with ActiveInteraction pretty much like in https://github.com/AaronLasseigne/active_interaction/issues/395
However by testing different syntaxes in ActiveInteraction I stumbled upon a much more peculiar issue that happens even in vanilly Ruby.
A non-executed line (blocked by a if false) can still have a major impact on the rest of the code:
class A
attr_accessor :a
def run
(puts defined? a; a) if true
end
def run2
(puts 'change a'; a = 0) if false
puts defined? a
a
end
end
x = A.new
x.run # "method"; nil
x.run2 # "local-variable"; nil
x.a = 5
x.run # "method"; 5
x.run2 # "local-variable"; nil
Can anyone explain if this is a bug or a feature? And if a feature: how come? It seems very odd.
EDIT: Thanks to the answer of #Sergio Tulentsev I managed to find that my question is pretty much a duplicate of Ruby instance method & conditional local variable assignment with same name with a different focus for the title name.
[is this] a bug or a feature?
Neither. It's a... peculiarity. What happens is, when parser sees assignment to local variable in the code, it goes ahead and adds the name to the scope (starting from that line, possibly shadowing other names, like your method here). With default value of nil. If the actual assignment is then never executed, the new local variable is still in scope and still evaluates to nil.
This is documented in https://docs.ruby-lang.org/en/2.5.0/syntax/assignment_rdoc.html#label-Local+Variables+and+Methods.

Ruby lexical scope inside iterator block [duplicate]

This question already has answers here:
Why can I refer to a variable outside of an if/unless/case statement that never ran?
(3 answers)
Closed 5 years ago.
In Ruby (v2.5.0)...
[1,2,3].map do |i|
if i.eql?(3)
a = 123
end
defined?(a)
end
=> ["local-variable", "local-variable", "local-variable"]
Can someone please explain to me how a can be a local-variable (equal to nil) in the first and second iteration, if it's not set until the third iteration?
Thanks in advance!
I will answer quoting a book by A.Black: Well Grounded Rubyist, Chapter 6, p. 158. (second edition 2014):
When the Ruby parser sees the sequence identifier, equal-sign, and value, as in this expression,
a = 123
it allocates space for a local variable a. The creation of the variable - not the assignment of a value to it, but the internal creation of a variable - always takes place as a result of this kind of expression, event if the code isn't executed.

How does precedence apply on an expression with an assignment and a conditional? [duplicate]

This question already has an answer here:
How is a local variable created even when IF condition evaluates to false in Ruby? [duplicate]
(1 answer)
Closed 6 years ago.
I have the following simple snippet:
var = 1 if false
I would expect this to evaluate as:
(var = 1) if false
so var would be undefined. However, var gets defined and receives a nil as its value.
What am I missing here?
Ruby recognizes local variables during parsing. So, in your case, even thouogh it's not set to 1 (because the precedence of this expression is like you wrote), ruby knows that it's local variable and doesn't raise NameError.
Ruby parser defines var when it sees it on the lefthand side of an expression (even though its inside of a conditional that doesn’t run). So nil looks an appropriate value.

Variable declaration behavior in if clause Ruby [duplicate]

This question already has answers here:
Confusion with the assignment operation inside a falsy `if` block [duplicate]
(3 answers)
Ruby: method inexplicably overwritten and set to nil
(2 answers)
Closed 9 years ago.
I am bit baffled about the below behavior
if true
foo = "true"
end
if false
bar = "false"
end
foo # => "true"
bar # => nil
buzz # =>
# ~> -:11:in `<main>': undefined local variable or method `buzz' for main:Object (NameError)
I expect bar to throw a NameError but it does not, I actually hit this when I was playing around with some friends but nobody could give a clear explanation to this behavior, thanks in anticipation of a clear answer
In ruby, the variables declared inside the if block have the exact same scope as the variables which are declared at the top level inside a method. Since the interpreter has gone through the line where it has seen bar, its fine with it.

How does ruby keep track of variables [duplicate]

This question already has answers here:
Confusion with the assignment operation inside a falsy `if` block [duplicate]
(3 answers)
Closed 9 years ago.
I am confused with the way Ruby keeps track of variables. For example:
case 1:
if true
a
end
will give you an error saying undefined local variable or method a.
case 2:
if false
a
end
a
will give you same the error for the second a, not for the first a.
case 3:
if false
a=2
end
a #=> nil
defined? a #=> 'local-variable'
If you compare case 2 and case 3, in case 2 it ignored the error first a. I think its because of ruby's execution path has not reached the variable a due to false in condition. Same thing when I do with assignment in case 3. It gives me variable a defined but with nil value. Can someone explain the way it works?
In parse time if Ruby found any assignment such a=2,then local variable is created at that moment.It does not matter if you put in inside of any false conditional expression or not. Otherwise a legitimate error will be thrown as undefined local variable or method a,if you try to use the variable such as a here,before it's creation with the assignment(=) operator.
Look Confusion with the assignment operation inside the fallacy if block

Resources