-2

In the following code, a is initialized outside of times, but times creates an inner scope, and a is accessible:

a = 5
3.times do |n|
  a = 3
end
a # => 3

The return value of a is 3 because a is available from the scope created by 3.times do ... end, which allows re-assigning the value of a. In fact, it re-assigned a to 3 three times.

Why is the following different from above?

a = 5
def adder(num)
  num = 3
end
adder(a) # => 3
a # => 5

It is because we bring in a, but it does not change the local variable, maybe because its a method. I don't know. Why is a 5 and not 3?

sawa
  • 165,429
  • 45
  • 277
  • 381
whiskey 4
  • 57
  • 5
  • Why is your method called `adder`? It doesn't add anything. – Stefan Jan 25 '16 at 08:08
  • yea sorry I just was trying to figure out scoping rules. Disregard the dumb method name, I thought I understood scoping rules in ruby but I am confusing myself I think. The rules aren't clear to me. does a=3 in the first example because there is scope leak in blocks? Or is it because of the a=3 reassignment? Then in the 2nd example does a = > 5 at the end because variables are passed into methods by value not by reference? what if there was a << operation inside the method would that affect the value of a? Its so confusing – whiskey 4 Jan 25 '16 at 09:00
  • You can modify (mutate) a passed object via its methods, e.g. `array << element`. But you can't reassign the outer scope's variable from within a method. Trying to do so will just create a local variable in the method's inner scope. See https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing – Stefan Jan 25 '16 at 09:23

3 Answers3

2

All Ruby variables without a glyph (@, @@, $) are local to the current method, module, class, or current program.

a = 5  # local to program

def adder
  # local to method
  num = 3
  puts num
end
adder(1)
# => 3

module Foo
  # local to module
  num = 2
  puts num
end
# => 2

class Bar
  # local to class
  num = 1
  puts num
end
# => 1

a      # local to program
# => 5

Within those, variables are scoped to the block where they are introduced, as you note yourself.

Amadan
  • 191,408
  • 23
  • 240
  • 301
1

Your two examples are not the same. First one is a method invocation with a block. Blocks are closures, meaning they remember context in which they are defined in (retaining references to variables in that scope, in particular).

The second one is a method definition. It is not a closure. In fact, it's a so-called "scope gate". Which means when definition of a method(/class/module) starts, all previously known local variables are pushed out of scope. So, if you were to access a from inside adder, you'd get a NameError.

And also, as @Atri noted, your method definition has no chances to work as you expect. You don't even assign to a there. So how do you expect a to change?

Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
0

Why is a 5 and not 3?

a = 5
def adder(num)
  num = 3
end
adder(a) # => 3
a # => 5

Because when you call adder with a, it is pass by value. So when the value of num changes, it doesn't change the value of a.

if you want a to change value, you need to do a = adder(a).

Community
  • 1
  • 1
Atri
  • 5,511
  • 5
  • 30
  • 40
  • ok that makes sense, so lets say this method was given an array as a parameter, and we modify that array using << inside the method. Then this would mutate that variable correct? So if I asked for that array again later, its values would have changed since now we mutated the array. it is being passed by reference not value. Is that right? – whiskey 4 Jan 25 '16 at 09:06
  • 1
    "Then this would mutate that variable correct?" – No, it would mutate the *array* that the variable *points to*. It would *not* mutate the variable. "So if I asked for that array again later, its values would have changed since now we mutated the array." – Yes, exactly. Because you mutated the *array*, and *not* because you mutated the variable. You cannot mutate a variable with a method call (`<<` is just a method call), *only* with an assignment. … – Jörg W Mittag Jan 25 '16 at 10:05
  • 1
    … "it is being passed by reference not value." – No. Everything is always passed by value in Ruby. When you mutate the array using `<<`, you are mutating the array. You are *not* changing the variable. The variable still points to the exact same object as it did before. It's just that the object has a different internal state. That has nothing to do with pass-by-reference. That's just mutable state. Ruby is not a purely functional language, you *can* mutate the state of objects. – Jörg W Mittag Jan 25 '16 at 10:05