Ruby Koans: Explicit Scoping on a Class Definition Part 2

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

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 - 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.

Why does @Array.clear not work

When running @old_values = @values, both variables point to the exactly same object. Now, when you assign a new array to @values as in @values = [], @values now is a different array object.

However when running @values.clear, you are changing the existing array, i.e. the same @old_values still points to. In this case, when adding new entries to @values, you are adding them to @old_values too (as they both point to the same object).

You should read a bit more about how Ruby handles variables and objects. This is part of the first lessons of about all Ruby tutorials.



Related Topics



Leave a reply



Submit