What does "shadowing" mean in Ruby? - ruby

If I do the following with warnings turned on under Ruby 1.9:
$VERBOSE = true
x = 42
5.times{|x| puts x}
I get
warning: shadowing outer local variable - x
Presumably it's to do with using x as a block parameter as well as a variable outside of the block, but what does "shadowing" mean?

Shadowing is when you have two different local variables with the same name. It is said that the variable defined in the inner scope "shadows" the one in the outer scope (because the outer variable is now no longer accessible as long as the inner variable is in scope, even though it would otherwise be in scope).
So in your case, you can't access the outer x variable in your block, because you have an inner variable with the same name.

Shadowing is more general term, it is applicable outside the Ruby world too. Shadowing means that the name you use in an outer scope - x = 42 is "shadowed" by local one, therefore makes in non accessible and confusing.

Related

In Ruby, what does a top-level assignment do?

I have the following code at the top-level of a .rb file:
class Times
def initialize(n)
#n = n
end
def each()
(1..#n).each {yield}
end
end
three_times = Times.new(3)
def f()
Times.new(3).each {puts 'Test'}
end
f()
This works, and prints 'Test' three times, as expected. However, if I replace Times.new(3) in f with three_times, i.e. three_times.each {puts 'Test'}, I get an error:
`f': undefined local variable or method `three_times' for main:Object (NameError)
Why does this not work? Why is Times accessible from within f, but not three_times?
More generally, what exactly does an assignment at the top level (e.g. three_times = Times.new(3)) do?
Because
three_times is a local variable
local variables are only accessible inside of a specific scope
def in ruby creates a new scope
So when f is invoked, it does not see or have access to three_times
To access three_times change it to either a global variable $three_times or an instance variable #three_times
The reason that you are able to reference the class Times is that it is a constant and ruby goes through a separate process of lookup for constants.
Sidestepping issue with def
You could also access the local variable by using a block to define your method, which sidesteps the whole scope gate issue. I do this sometimes when writing rake tasks but rarely do it outside of scripts.
three_times = Times.new(3)
define_method :foo do
three_times.each { puts 'Tests'}
end
foo
Why does this not work? Why is Times accessible from within f, but not three_times?
Variables whose name starts with a lowercase letter are local variables. Local variables are local to the scope they are defined in (that's why they are called local variables.)
Variables whose name starts with an uppercase letter are constants. Constants are looked up first in the default constant scope, then lexically outwards, then dynamically upwards by inheritance.
More generally, what exactly does an assignment at the top level (e.g. three_times = Times.new(3)) do?
Nothing special. It does the same thing that an assignment anywhere else does. In this case, it:
Dereferences the variable (constant) Times, let's call this object o1.
Evaluates the literal integer expression 3, let's call the resulting object o2.
Sends the message new to o1, passing o2 as an argument. Let's call the answer to that message send o3.
Binds o3 to the local variable named three_times.
As you can see, there's nothing in there that is somehow specific to script scope or the top-level.
It's because it's looking for a local variable called "three_times". If you wish to make "three_times" to be "top-level" or "global", prepend the variable name with $ so that it's "$three_times".
Your code works for me, there is no errors. You can call f() sucecsfully

Ruby loop local variables and inmutability

I have the following code:
# Assuming each element in foo is an array.
foo.each do |bar|
zaz = bar.first
# Other code using zaz, but not modifying it.
end
Will zaz local variable be modified on each iteration inside this loop, making it mutable? I am not sure about the behavior of Ruby here.
It depends on the code before the loop, really.
If that is all the code, then zaz is a block-local variable, and a new zaz variable will be created every time the loop body is evaluated.
If, however, there is a zaz local variable in the surrounding scope, then zaz is a free variable in the block, and since block scopes nest in their surrounding scope, the existing zaz variable outside the block will be re-assigned over and over again, every time the block is evaluated.
You can ensure that zaz is always treated as a block-local variable and never looked up in the surrounding scope, by explicitly declaring it as a block-local variable in the block's parameter list:
foo.each do |bar; zaz|
zaz = bar.first
end
Note, however, that your code only makes sense IFF your code is impure and mutable:
You assign to zaz but never actually use it inside the block. So, the only way that this makes sense at all is if zaz is a local variable in the outer scope and you are assigning it. Although in that case, your entire loop is just equivalent to zaz = foo.last.first.
each evaluates the block only for its side-effects. Without side-effects, each makes no sense at all, so the fact that you are using each implies that you have side-effects.
Note that the term "immutable" without additional qualification usually refers to values. When talking about "immutable variables", we usually say "immutable variable" explicitly, to make clear that we are only talking about whether or not a variable can be re-bound, not about mutating object state. Or, one could just say "constant", which is the technical term for "immutable variable" … although that term already has a specific meaning in Ruby.
each loops often mutate the object. Each has to do something.
Because each doesn't return anything useful - it returns the array itself, It won't mutate the object if it sends every element somewhere, like to be printed on screen.
foo.each do |bar|
# do something with element like
# print it, change it, save it
end
Functional alterantive is map
foo.map { |bar| bar.something }
It returns new array which is original array processed in immutable way. Obviously you have to be careful to use immutable methods. This would not be immutable:
foo.map { |bar| bar.something! }
Here something! does something destructive to the element of array.
However I have never seen map used like that. Use each for something destructive.

how to use truly local variables in ruby proc/lambda

Beginner Ruby question. What is the simplest way to change this code, but leaving the block completely intact, that eliminates the side effect?
x = lambda { |v| x = 2 ; v}
x.call(3)
#=> 3
x
#=> 2
This is the simplest example I could contrive to illustrate my issue, so "remove the assignment" or "don't assign the Proc to x" is not what I'm looking for.
I want to set local variables in a Proc (or lambda) that can be assigned without affecting the original enclosing scope. I could dynamically create a class or module to wrap the block, but that seems overkill for such a basic thing.
Equivalent Python to what I'm trying to do:
def x(v):
x = 2 # this is a local variable, what a concept
return v
Sometimes it is the desired behavior:
total = 0
(1..10).each{|x| total += x}
puts total
But sometimes it's accidental and you don't want to mess with an outside variable which happens to have the same name. In that case, follow the list of parameters with a semicolon and a list of the block-local variables:
x = lambda{|v; x| x = 2; v}
p x.call(3) #3
p x #<Proc:0x83f7570#test1.rb:2 (lambda)>
The reason for this is that the lambda is bound to its defining scope (NOT its calling scope), and is a full closure, which, among other things, includes the local variable x. What you really want here is an unbound proc to pass around and call without any particular binding. This isn't something that Ruby does very easily at all, by design (it's possible with eval, but that's less of a block and more just of a string statement). Procs, lambdas, and blocks are all bound to their defining scope. Lexical scope is only established on classes, modules, and methods; not on blocks/procs/lambdas/anything else.
It's worth noting that Python doesn't even permit assignment in lambdas in the first place.

Why can I refer to a variable outside of an if/unless/case statement that never ran?

Why does the following code not throw an error?
if false
x = 0
end
x #=> nil
Whereas the following does throw an error:
y # NameError: undefined local variable or method `y' for main:Object
The same thing happens with unless & case statements.
It's because of how the Ruby parser works. Variables are defined by the parser, which walks through the code line-by-line, regardless of whether it will actually be executed.
Once the parser sees x =, it defines the local variable x (with value nil) henceforth in the current scope. Since if/unless/case/for/while do not create a new scope, x is defined and available outside the code block. And since the inner block is never evaluated as the conditional is false, x is not assigned to (and is thus nil).
Here's a similar example:
defined?(x) and x = 0
x #=> nil
Note that this is a rather high-level overview of what happens, and isn't necessarily exactly how the parser works.
This has to do with a quirk of Ruby's scoping rules.
In ruby, an undecorated variable x appearing by itself could either be a local variable or a method call -- the grammar can't tell which. It's up to the parser to figure it out as it resolves local variable references. The rule is simple: if an assignment to a variable of the same name has been seen already in the local scope, then the reference is a local variable, and the reference is bound to that local variable. Otherwise, it's a method call, and it will be looked up as such at runtime.
Local variable references in Ruby are optimized into array lookups (each local variable is assigned a 'slot', and bound local variable references generated by the parser are converted into slot references). The array is initialized with all nil:
/* initialize local variables */
for (i=0; i < local_size; i++) {
*sp++ = Qnil;
}
Thus, if you refer to a local variable that hasn't been assigned, through a bound local reference (which can only happen if there was a skipped assignment above the reference in the same local scope), you get nil.
I thought your question was interesting so I tried to look it up and found this:
I don't understand ruby local scope
The correct answer seems to be put Jorg.
Lets look at what happens when you try to access a variable that isn't initialized:
NameError: undefined local variable or method `UNDECLAREDVAR' for main:Object
The exception states that it is unavailable to evaluate whether a variable or method. The reason it doesn't throw the same exception is because uninitialized local variables are set to nil. So puts x is a okay because the interpretor knows that x is variable but uninitialized and not a method.

Ruby if vs end of the line if behave differently?

Why doesn't this code work?
b if b = true
Error: undefined local variable or method `b'
But this does:
if b = true
b
end
Shouldn't they be the same?
This is a very good question. It has to do with the scoping of variables in Ruby.
Here is a post by Matz on the Ruby bug tracker about this:
local variable scope determined up to down, left to right. So a local variable first assigned in the condition of if modifier is not effective in the left side if body. It's a spec.
In the first version as soon as k is hit, the parser pukes because it hasn't been seen yet.
In the second version, k is part of an assignment expression, and is parsed differently.
I don't know the reason but the problem that the interpreter tries to lookup the variable k before evaluating the condition.
If you write it like this, there won't be any error and works as you expected:
k = nil
h = {k: 1}
v = k if k = h.delete(:k)
you have put only one '='
Try with '=='
Then you will get error
In second example, you are assigning 'true' to b.
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.
Local variables are created in that parsing pass. It's 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.

Resources