Ruby - Lexical Scope VS Inheritance

Ruby - Lexical scope vs Inheritance

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”

Ruby Koans - Continuation of Lexical Scope vs Inheritance Hierarchy

In strictly lexically/statically scoped languages like C++ or Java, identifiers are resolved by simply checking the current scope, ascending one scope higher and repeating until the base most scope is reached. For example: if your example were C++, LEGS would be searched for first in the calling class Bird/Oyster, then Animal, then My Animal.

Ruby has a kind of dynamic scoping. Each object, even if it resides in the same 'place' can have its own scope lookup order depending on how it was defined or create at runtime. You can think of each method as having a stack of scopes to search, and how it was defined pushes new scopes onto that stack.

Because of the way Bird is defined it gets (BaseScope is not its real name, you did not provide enough code to provide this) BaseScope::MyAnimals::Bird, BaseScope::MyAnimals::Animal, BaseScope::MyAnimals and BaseScope to search through for resolving names.

While The first Oyster only gets BaseScope::MyAnimals::Oyster, BaseScope::MyAnimals::Animal and BaseScope.

The Oyster without inheritance gets even less, just BaseScope::MyAnimals::Oyster and BaseScope.

Each use of the class keyword and inheritance in this example pushes another scope to check onto the stack of scopes for its contents to search. So using class MyAnimals::Oyster only pushed one entry onto this stack.

Edit

For simplicity I left out the method legs_in_oyster. It is a scope that could be searched. It's trivial definition is self explanatory and including it would add much useless text to this answer.

I also left out the global scope for simplicity. I know Koans has at least one scope at or between BaseScope and the global scope.

Ruby: explicit scoping on a class definition

I think this example explains it best. Ruby searches for the constant definition in this order:

  1. The enclosing scope
  2. Any outer scopes (repeat until top level is reached) Any outer scopes (up to but not including the top level
  3. Included modules
  4. Superclass(es)
  5. Top level
  6. Object
  7. Kernel

EDIT

Thanks to Mark Amery for pointing out this error. The top-level is only reached in the case where there are no enclosing scopes and/or superclasses. The linked example actually makes this clear, sadly I read it wrong.

An example for this case:

FOO = 'I pity the foo!'

module One
FOO = 'one'

class Two
FOO = 'two'

def self.foo
FOO
end
end

class Three < Two
def self.foo
FOO
end
end
end

class Four
class Five < Four
def self.foo
FOO
end
end
end

describe FOO do
it "depends where it is defined" do
expect(FOO).to eq 'I pity the foo!' # top-level
expect(One::FOO).to eq 'one' # module
expect(One::Two.foo).to eq 'two' # class
expect(One::Three.foo).to eq 'one' # outer scope (One) comes before superclass
expect(Four::Five.foo).to eq 'I pity the foo!' # top-level
end
end

Ruby Koans: explicit scoping on a class definition part 2

I was just pondering the very same question from the very same koan. I am no expert at scoping, but the following simple explanation made a lot of sense to me, and maybe it will help you as well.

When you define MyAnimals::Oyster you are still in the global scope, so ruby has no knowledge of the LEGS value set to 2 in MyAnimals because you never actually are in the scope of MyAnimals (a little counterintuitive).

However, things would be different if you were to define Oyster this way:

class MyAnimals
class Oyster < Animal
def legs_in_oyster
LEGS # => 2
end
end
end

The difference is that in the code above, by the time you define Oyster, you have dropped into the scope of MyAnimals, so ruby knows that LEGS refers to MyAnimals::LEGS (2) and not Animal::LEGS (4).

FYI, I got this insight from the following URL (referenced in the question you linked to):

  • https://groups.google.com/forum/#!topic/comp.lang.ruby/t5rtDNol3P8

Preference of code style in module nesting

These two methods of writing get confused quite often.

First is to say, that to my best knowledge there is no measurable performance difference. (one constant look-up in the below written example)

The most obvious difference, probably the most known, is that your second example (module A::B) requires for the module A to exist in the time of the definition.

Otherwise than than that most people think they are interchangeable. That is not true.

Modules are simply constants in ruby and so regular constant look-up applies.

Let me show it on an example:

module A
class Test
end

module B
class Show
p Module.nesting # =>[A::B::Show, A::B, A]

def show_action
Test.respond_with(%q(I'm here!))
end
end
end
end

On the other hand if you call it via the A::B look what happens:

module A
class Test
end
end

module A::B
class Show
p Module.nesting # => [A::B::Show, A::B]

def show_action
Test.respond_with(%q(I'm here!))
end
end
end

The difference is that .nesting produces:

1) in the first case: [A::B::Show, A::B, A] (you are nested in module A)

2) in the second case: [A::B::Show, A::B] (here not)

Module methods in Ruby

This is how I see it:

Dollar::line

There is no such method defined in this module so It's calling At::line because you included this module.

Star::line

It uses last defining from Dollar module(it goes after original Star definition so it's overridden).

Dollar::line

Third call is the same as the first one.

line

And the last one is At::line because You made an include.

When is Lexical Scope for a function within a function determined?

When f is run, the first thing that happens is that a function g is created in f's local environment. Next, the variable z is created by assignment.

Finally, x is added to the result of g(x) and returned. At the point that g(x) is called, x = 3 and g exists in f's local environment. When the free variable z is encountered while executing g(x), R looks in the next environment up, the calling environment, which is f's local environment. It finds z there and proceeds, returning 7. It then adds this to x which is 3.

(Since this answer is attracting more attention, I should add that my language was a bit loose when talking about what x "equals" at various points that probably do not accurately reflect R's delayed evaluation of arguments. x will be equal to 3 once the value is needed.)

Using :: instead of module ... for Ruby namespacing

If you use class Foo::Bar, but the Foo module hasn't been defined yet, an exception will be raised, whereas the module Foo; class Bar method will define Foo if it hasn't been defined yet.

Also, with the block format, you could define multiple classes within:

module Foo
class Bar; end
class Baz; end
end


Related Topics



Leave a reply



Submit