Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby - Lexical scope vs Inheritance

This is a continuation this original SO question: Using "::" instead of "module ..." for Ruby namespacing

In the original SO question, here is the scenario presented which I'm still having trouble understanding:

FOO = 123

module Foo
  FOO = 555
end

module Foo
  class Bar
    def baz
      puts FOO
    end
  end
end

class Foo::Bar
  def glorf
    puts FOO
  end
end

puts Foo::Bar.new.baz    # -> 555
puts Foo::Bar.new.glorf  # -> 123

Can someone provide some explanation behind why the first call is returning 555 and why the second call is returning 123?

like image 770
wmock Avatar asked Feb 27 '13 18:02

wmock


People also ask

Is Ruby lexically scoped?

Ruby, like most modern programming languages, uses a static scope, often called lexical scope (as opposed to dynamic scope). The current scope is based on the structure of the code and determines the variables available at specific parts of the code.

What does lexical scope mean in Ruby?

If two lines are lexically scoped, it simply means that they occur within the same code block — regardless of what that code block may evaluate to.

Is Ruby dynamic binding?

Smalltalk, Objective-C, Modula-3, Python, and Ruby use dynamic method binding for all methods.


2 Answers

You can think of each appearance of module Something, class Something or def something as a “gateway” into a new scope. When Ruby is searching for the definition of a name that has been referenced it first looks in the current scope (the method, class or module), and if it isn’t found there it will go back through each containing “gateway” and search the scope there.

In your example the method baz is defined as

module Foo
  class Bar
    def baz
      puts FOO
    end
  end
end

So when trying to determine the value of FOO, first the class Bar is checked, and since Bar doesn’t contain a FOO the search moves up through the “class Bar gateway” into the Foo module which is the containing scope. Foo does contain a constant FOO (555) so this is the result you see.

The method glorf is defined as:

class Foo::Bar
  def glorf
    puts FOO
  end
end

Here the “gateway” is class Foo::Bar, so when FOO isn’t found inside Bar the “gateway” passes through the Foo module and straight into the top level, where there is another FOO (123) which is what is displayed.

Note how using class Foo::Bar creates a single “gateway”, skipping over the scope of Foo, but module Foo; class Bar ... opens two separate “gateways”

like image 53
matt Avatar answered Nov 02 '22 01:11

matt


wow, great question. The best answer I can come up with is in this case you're using the module to define a namespace.

Check this out:

FOO = 123

module Foo
  FOO = 555
end

module Foo
  class Bar
    def baz
      puts FOO
    end

    def glorf3
      puts ::FOO
    end
  end
end

class Foo::Bar
  def glorf2
    puts Foo::FOO
  end

  def glorf
    puts FOO
  end
end

puts Foo::Bar.new.baz    # -> 555
puts Foo::Bar.new.glorf  # -> 123
puts Foo::Bar.new.glorf2  # -> 555
puts Foo::Bar.new.glorf3  # -> 123

So my thought is that when you define:

module Foo
  FOO = 555
end

you are creating FOO in the namespace of Foo. So when you use it here:

module Foo
  class Bar
    def baz
      puts FOO
    end
  end
end

you are in the Foo namespace. However, when you refer to it in:

class Foo::Bar
  def glorf
    puts FOO
  end
end

FOO is coming from the default namespace (as illustrated by ::FOO).

like image 6
rainkinz Avatar answered Nov 02 '22 03:11

rainkinz