Jikku Jose
Jikku Jose

Reputation: 18804

Scope of method defined inside another method

Consider the following code:

class Emotion
  def confused?
    def confused?
      'yes'
    end
    'no'
  end
end

am_i = Emotion.new

am_i.confused? # => "no"
am_i.confused? # => "yes"

5.times.map { Emotion.new.confused? } # => ["yes", "yes", "yes", "yes", "yes"]

Explanation:

  1. Running confused? the first time returns "no" and re-defines itself.
  2. Running confused? the second time invokes the new method that returns "yes".

Though the above functionality is clear, I am not sure how both methods are being defined to the same scope.

The reason I ask is because:

Upvotes: 2

Views: 403

Answers (1)

Adrian
Adrian

Reputation: 15171

A variable cannot be accessed in similar fashion inside and outside a method; for instance: if I define @variable inside and outside a method it has different meanings.

When setting instance variables with @x = y syntax, the instance variables are set for self.

I have learned that method definition changes self. Won't the inside definition of the confused? method have a different scope in comparison with the outside definition?

The scope can change, but in this case it doesn't. The value of self inside a method is always the object that the method is being called on. In both of your confused? method definitions, self is an instance of Emotion.

How does def actually determine where to define the method?

The answer to this (at least in MRI) is actually somewhat unintuitive. Every scope has a reference to a "default definee" object which is not necessarily related to self and is always an instance of Module. def in that scope will define a method for the default definee. The most trivial way to get the default definee in ruby code is this:

eval("def __m; end; m = method(:__m); undef __m; m.owner")

For example, running that at the top level of a ruby program returns Object. So, the default definee for the toplevel scope is Object.

Here is some code which can hopefully answer your questions about scopes:

# `self` is the toplevel object
# default definee is `Object`

class X
  # `self` is X
  # default definee is also X

  @a = 1 # defines instance variable @a for `X`

  def y # defines method 'y' on X

    # `self` is an instance of X
    # default definee is X

    @b = 2 # defines instance variable @b for an instance of `X`

    def z # defines method 'z' on X

      # `self` is still an instance of X
      # default definee is still X

      @c = 3 # defines instance variable @c for an instance of `X`

    end
  end

  class << self
    # `self` is the metaclass of X
    # default definee is also the metaclass of X

    @d = 4 # defines instance variable @d for the metaclass of X
  end
end

It is important to remember that methods are always stored in instances of Module, where as instance variables can be stored in any object (excluding primitives like nil or booleans or integers).

Upvotes: 3

Related Questions