1

This is from the example given in ruby-doc section 4.3, in the following link:

Ruby-Doc

The explanation goes like this:

Ruby looks for assignment statements. If at some point in the source prior to the use of a'' it sees it being assigned to, it decides to parsea'' as a variable, otherwise it treats it as a method.

the example is below

def a
  print "Function 'a' called\n"
  99
end

for i in 1..2
  if i == 2
    print "a=", a, "\n"
  else
    a = 1
    print "a=", a, "\n"
  end
end

The output is given as

a=1
Function 'a' called
a=99

But as evident from the code, when i is 1, a is assigned to 1 an 1 is printed as value of a. Then for i = 2, method 'a' is called.

Now what will happen if I print 'a' outside the for loop? I got the value 1, but I have no clue how that is possible. If reassigning a to 1 from the previous value of method is going to change it everywhere, then during i = 2 also the output should've been 1. Correct me if I'm wrong.

Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
Vasanth
  • 108
  • 7

3 Answers3

1

When you assign a = 1 you are "overriding" the function a. I hope this small piece of code can help:

def a
  'function a'
end

p a.class #=> String returned by the function
a = 1
p a.class #=> Integer "overridden" by a=1
p a().class #=> String the function is not lost, needs to be called with round brackets.
iGian
  • 11,023
  • 3
  • 21
  • 36
  • Thank you. This was helpful. I'd like to add that determination of whether a is a variable or a method depends on where the usage occurs on the source code rather than at what point of execution it was assigned. In the example, when i = 2 happens after a is assigned in the else part. Hence in the if part, method 'a' is called. – Vasanth Mar 09 '19 at 19:49
  • @Vasanth try swap the order of the statements. Move up the `a = 1 etc.` upon condition `if i == 1`. – iGian Mar 09 '19 at 20:23
  • @Vasanth: yes, as you observed, the shadowing happens _lexically_. If usage is before first assignment (in terms of position in the source file), then local variable doesn't shadow the previously known value – Sergio Tulentsev Mar 09 '19 at 21:39
  • 1
    @Vasanth I forget to mention that the function call with round brackets is still available. See my edit. – iGian Mar 10 '19 at 19:06
0

There is nothing "local" in your example. In ruby most things are accessible unless they are defined inside a class or module definition where they than can be "localized". Here is how you might write simple class

class Foo
  def self.a
    'a'
  end
end

Here we're just defining a single class method which will always return the string a which cannot be modified. If you want encapsulation, it is recommended not to write things without it. Otherwise you can pretty much modify anything anywhere in Ruby, which is why it is a good language for metaprogramming. For other ideas maybe have a look here

lacostenycoder
  • 10,623
  • 4
  • 31
  • 48
0

Assignment statements in unreached code branches do indeed affect the local variables.

class Foo
  def method_missing(meth, *args)
    "returned from method_missing #{meth}"
  end
end

Foo.new.instance_exec do
  puts abc.inspect # abc is considered a method

  if true == false
    abc = 1 # <-- this should never be reached
  end

  puts abc.inspect # abc is considered a local variable
end

This outputs:

"returned from method_missing abc" # <- the return value from method_missing
nil # <- suddenly abc is nil, even though abc wasn't touched.

So, Ruby sees code that assigns to a local variable abc and decides that from that point on abc is a variable, not a method call.

I believe this is done to allow something like this without NoMethodError:

if something
  a = true
end

if a
  puts "ah yes, a."
end

It's uncommon to bump into this unless you're doing something silly in the first place.

Kimmo Lehto
  • 5,910
  • 1
  • 23
  • 32