Scope of method defined inside another method - ruby

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:
Running confused? the first time returns "no" and re-defines itself.
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:
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.
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?
How does def actually determine where to define the method?

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

Related

What does it mean creating an instance variable outside a class and inside a method?

What does it mean in the ruby language to create an instance variable outside of a class?
e.g:
def my_method:
#animal = "cat"
end
I've also seen it written outside of a method, like this:
#foo = "bar"
Is this just sugar syntax?
thanks!
what it means to create instance of variable outside class in ruby?
Instance variables have nothing to do with classes. They belong to objects, i.e. instances, that's why they are called "instance" variables.
def my_method
#animal = "cat"
end
This does not create an instance variable. It creates a method named my_method, which when you call it assigns to the instance variable named #animal.
There is nothing special about this. When you call this method, it will assign to the #animal instance variable of whatever object you call this method on, just like any other method that assigns an instance variable.
For example here:
class Foo
def my_method
#animal = 'cat'
end
end
foo = Foo.new
bar = Foo.new
If you call foo.my_method, it will assign to the instance variable #animal of foo, if you call bar.my_method, it will assign to the instance variable #animal of bar.
There is actually nothing different between those two examples as far as the instance variable is concerned.
The only interesting question is "Which class is the method defined in?" But that has nothing to do with instance variables.
Methods defined on the top-level become private instance methods of Object.
#foo = "bar"
Again, this is no different than any other assignment to an instance variable. This assigns to the instance variable named #foo of whatever object self is at the moment.
From an instance variable standpoint, there is absolutely nothing special about this. #foo = 'bar' always means "assign to the instance variable named #foo of whatever object self is at the moment". There is nothing else it can mean.
The only interesting question is: "What is self at this point?" But that has nothing to do with instance variables.
At the top-level, self is always the anonymous object commonly called main in the Ruby community.
is just sugar sintaxe?
No, this is not syntactic sugar for anything. It's just an assignment like any other assignment. There is absolutely nothing special about it.
If you do this on the command line:
% ruby -e "puts self; puts self.class"
main
Object
You'll see at the top-level scope (not inside a class or module), you'll be in the scope of an instance of Object called main.
What you already know about using instance variables and defining methods etc are the same for main as it is for class and module.
class Foo
end
foo_main = Foo.new # create instance of `Foo`
class Foo
self # then, inside scope of class `Foo`
# => Foo
def foo # define instance method for class `Foo`
puts "foo"
end
end
foo_main.foo # calls method on an instance of `Foo`
# => foo
# at the top level, it's as if the Ruby runtime has already done something
# like "Object.new" and then put you "inside" the created instance
my_main = self # inside scope of `main` already
# => main
def bar # define instance method for class `Object`
puts "bar"
end
# below are all equivalent
my_main.bar
self.bar
bar # implied `self` is main, an instance of `Object`
You may have noticed:
inside Foo, self returns Foo, a class
at top-level, self returns main, an instance of class Object
Although it seems like they're two different things, what makes Ruby work the way it does is they're actually the same! More specifically, it's:
inside Foo, self returns Foo, an instance of class Class
at top-level, self returns main, an instance of class Object

Difference between #foo, self.foo, and foo?

class Artist
##song_count = []
attr_accessor :name, :songs
def initialize(name)
#name = name
#songs = []
end
def add_song(song)
#songs << song
end
def print_songs
songs.each {|song| puts song.name}
end
end
So in this example, it uses all two types, #songs and songs.
I'm having a hard time understanding why these are used, instead of using #songs for everything.
And then in this example,
def add_song(song)
self.songs << song
song.artist = self
##song_count +=1
end
Why is self.songs used instead of #songs?
Ok, so I forgot to say one more thing. In the first code snippet above,for method print_songs, why am I able to use songs.each instead of #songs.each? I was expected it to generate an error undefined songs.
Why is self.songs used instead of #songs
Using the method is more flexible. You're abstracting yourself from knowing how exactly it gets/stores data. The less you rely on implementation details, the easier it will be for you to change code later.
One small example, consider this implementation of songs
def songs
#songs ||= []
#songs
end
#songs may or may not have been assigned value prior to invocation of this method. But it doesn't care. It makes sure that #songs does have a sane default value. The concept is called "lazy initialization" and it's very tedious and error-prone to do if you use instance variables directly.
So, when in doubt, always use methods.
Difference between foo and #foo
Instance variables
Instance variables are defined within instance methods, and their names begin with #. Their value is only accessible within the specific object on which it was set. In other words, when we modify the value of an instance variable, the change only applies to that particular instance. Unlike local variables which are only available within the method where they were defined, instance variables are accessible by all methods within the object (instance methods of the class). Instance variables are the most commonly used type of variable in Ruby classes.
class Car
attr_reader :color
def set_color(color_receiverd_as_argument)
#color = color_receiverd_as_argument
end
end
car1 = Car.new
car1.color # Output: => nil
car1.set_color "black"
car1.color # Output: => "black"
car2 = Car.new
car2.set_color "silver"
car2.color # Output: => "silver"
In the example above, notice that:
Trying to access an instance variable before it's initialized will not raise an exception. Its default value is nil.
Changing the value of the color variable in one instance of the Car class does not affect the value of the same variable in the other instances.
Local variables
A local variable within a class is like any other local variable in Ruby. It is only accessible within the exact scope on which it's created. If defined within a method, it is only available inside that method.
class Car
def initialize
wheels = 4
end
def print_wheels
print wheels
end
end
c = Car.new
c.print_wheels # Output: NameError: undefined local variable or method `wheels'…
The self keyword
The self keyword is always available, and it points to the current object. In Ruby, all method calls consist of a message sent to a receiver. In other words, all methods are invoked on an object. The object on which the method is called is the receiver, and the method is the message. If we call "foo".upcase, the "foo" object is the receiver and upcase is the message. If we don't specify an object (a receiver) when calling a method, it is implicitly called on the self object.
Self keyword at class level
When used within a class but outside any instance methods, self refers to the class itself.
class Foo
##self_at_class_level = self
def initialize
puts "self at class level is #{##self_at_class_level}"
end
end
f = Foo.new # Output: self at class level is Foo
Self keyword at instance methods
When inside an instance method, the self keyword refers to that specific instance. In other words, it refers to the object where it was called.
class Meditation
def initialize
puts "self within an instance method is #{self}"
end
end
zazen = Meditation.new # Output: self within an instance method is #<Meditation:0x00000000ab2b38>
Notice that #<Meditation:0x00000000ab2b38> is a string representation of the zazen object, which is an instance of the Meditation class.

Local variables in `class` definition scope versus `def` method scope

Here, I create a local variable in class scope:
class MyClass
x = 1
puts x
end
It prints 1 even if I don't create any instances of MyClass.
I want to use x in some method:
class MyClass
x = 1
def method
puts x
end
end
m = MyClass.new
m.method
And I can't. Why? I get that class definition creates a scope, but why is it not accessible in the method? Isn't scope of the method inside the scope of the class?
I can imagine that this is related to creation of a class. Since any class is an object of Class, maybe the scope of MyClass is the scope of some Class method, and the way of coupling methods of MyClass to that instance makes their scope completely different.
It also seems to me that I can't just create a scope with {} (like in C) or something like do..end. Am I correct?
Scope of a method is not inside the class. Each method has its own entirely new scope.
New scopes are created whenever you use the class, module, and def keywords. Using brackets, as in C, does not create a new scope, and in fact you cannot arbitrarily group lines of code using brackets. The brackets (or do...end) around a Ruby block create a block-level scope, where variables previously created in the surrounding scope are available, but variables created within the block scope do not escape into the surrounding scope afterward.
Instance methods share the scope of their instance variables with other instances methods. An instance variable defined in the scope of a class definition is available in class-level singleton methods, but not in instance methods of the class.
Illustration:
class Foo
x = 1 # available only here
#y = 2 # class-wide value
def self.class_x
#x # never set; nil value
end
def self.class_y
#y # class-wide value
end
def initialize(z)
x = 3 # available only here
#z = z # value for this instance only
end
def instance_x
#x # never set; nil
end
def instance_y
#y # never set; nil
end
def instance_z
#z # value for this instance only
end
end
Foo.class_x # => nil
Foo.class_y # => 2
Foo.new(0).instance_x # => nil
Foo.new(0).instance_y # => nil
foo3 = Foo.new(3)
foo4 = Foo.new(4)
foo3.instance_z # => 3
foo4.instance_z # => 4
You can access class-level instance variables from within instances using the class-level getter. Continuing the example above:
class Foo
def get_class_y
self.class.class_y
end
end
foo = Foo.new(0)
foo.get_class_y # => 2
There exists in Ruby the notion of a "class variable," which uses the ## sigil. In practice, there is almost never a reasonable use case for this language construct. Typically the goal can be better achieved using a class-level instance variable, as shown here.
Here, I create a local variable in class scope:
class MyClass
x = 1
puts x
end
It prints 1 even if I don't create any instances of MyClass.
Correct. The class definition body is executed when it is read. It's just code like any other code, there is nothing special about class definition bodies.
Ask yourself: how would methods like attr_reader/attr_writer/attr_accessor, alias_method, public/protected/private work otherwise? Heck, how would def work otherwise if it didn't get executed when the class is defined? (After all, def is just an expression like any other expression!)
That's why you can do stuff like this:
class FileReader
if operating_system == :windows
def blah; end
else
def blubb; end
end
end
I want to use x in some method:
class MyClass
x = 1
def method
puts x
end
end
m = MyClass.new
m.method
And I can't. Why? I get that class definition creates a scope, but why is it not accessible in the method? Isn't scope of the method inside the scope of the class?
No, it is not. There are 4 scopes in Ruby: script scope, module/class definition scope, method definition scope, and block/lambda scope. Only blocks/lambdas nest, all the others create new scopes.
I can imagine that this is related to creation of a class. Since any class is an object of Class, maybe the scope of MyClass is the scope of some Class method, and the way of coupling methods of MyClass to that instance makes their scope completely different.
Honestly, I don't fully understand what you are saying, but no, class definition scope is not method definition scope, class definition scope is class definition scope, and method definition scope is method definition scope.
It also seems to me that I can't just create a scope with {} (like in C) or something like do..end. Am I correct?
Like I said above: there are 4 scopes in Ruby. There is nothing like block scope in C. (The Ruby concept of "block" is something completely different than the C concept of "block.") The closest thing you can get is a JavaScript-inspired immediately-invoked lambda-literal, something like this:
foo = 1
-> {
bar = 2
foo + bar
}.()
# => 3
bar
# NameError
In general, that is not necessary in Ruby. In well-factored code, methods will be so small, that keeping track of local variables and their scopes and lifetimes is really not a big deal.
So just creating a class without any instances will lead to something
actually executing in runtime (even allocating may be)? That is very
not like C++. –
Check out this code:
Dog = Class.new do
attr_accessor :name
def initialize(name)
#name = name
end
end
If you execute that code, there won't be any output, but something still happened. For instance, a global variable named Dog was created, and it has a value. Here's the proof:
Dog = Class.new do
attr_accessor :name
def initialize(name)
#name = name
end
end
dog = Dog.new("Ralph")
puts dog.name
--output:--
Ralph
The assignment to the Dog constant above is equivalent to writing:
class Dog
...
...
end
And, in fact, ruby steps through each line inside the class definition and executes each line--unless the line of code is inside a def. The def is created but the code inside a def doesn't execute until the def is called.
A very common line you will see inside a class definition is:
attr_accessor :name
...which can be rewritten as:
attr_accessor(:name)
...which makes it obvious that it's a method call. Ruby executes that line--and calls the method--when you run a file containing the class definition. The attr_accessor method then dynamically creates and inserts a getter and a setter method into the class. At runtime. Yeah, this ain't C++ land anymore--welcome to NeverNever Land.
I get that class definition creates a scope, but why is it not
accessible in the method?
Because that is the way Matz decided things should be: a def creates a new scope, blocking visibility of variables outside the def. However, there are ways to open up the scope gates, so to speak: blocks can see the variables defined in the surrounding scope. Check out define_method():
class MyClass
x = 1
define_method(:do_stuff) do
puts x
end
end
m = MyClass.new
m.do_stuff
--output:--
1
The block is everything between do...end. In ruby, a block is a closure, which means that when a block is created, it captures the variables in the surrounding scope, and carries those variables with it until the the block is executed. A block is like an anonymous function, which gets passed to a method as an argument.
Note that if you use the Class.new trick, you can open two scope gates:
x = 1
MyClass = Class.new do
define_method(:do_stuff) do
puts x
end
end
m = MyClass.new
m.do_stuff
--output:--
1
Generally, ruby allows a programmer to do whatever they want, rules be damned.

Local variables in class definitions / scope

I was under the impression that class definitions in Ruby can be reopened:
class C
def x
puts 'x'
end
end
class C
def y
puts 'y'
end
end
This works as expected and y is added to the original class definition.
I'm confused as to why the following code doesn't work as expected:
class D
x = 12
end
class D
puts x
end
This will result in a NameError exception. Why is there a new local scope started when a class is reopened? This seems a little bit counterintuitive. Is there any way to continue the previous local scope when a class is extended?
Local variables are not associated with an object, they are associated with a scope.
Local variables are lexically scoped. What you are trying to do is no more valid than:
def foo
x = :hack if false # Ensure that x is a local variable
p x if $set # Won't be called the first time through
$set = x = 42 # Set the local variable and a global flag
p :done
end
foo #=> :done
foo #=> nil (x does not have the value from before)
#=> done
In the above it's the same method, on the same object, that's being executed both times. The self is unchanged. However, the local variables are cleared out between invocations.
Re-opening the class is like invoking a method again: you are in the same self scope, but you are starting a new local context. When you close the class D block with end, your local variables are discarded (unless they were closed over).
In ruby, local variables accessible only in scope that they are defined. However, the class keywords cause an entirely new scope.
class D
# One scope
x = 12
end
class D
# Another scope
puts x
end
So, you can't get access to local variable that defined in first class section, because when you leave first scope, local variable inside it is destroyed and memory is freed by garbage collection.
This is also true for the def, for example.
The easy solution would be to make x a class instance variable. Of course, this doesn't answer your scope question. I believe the reason x is not in the scope (see Detecting the Scope of a Ruby Variable) of the re-opened class is because its scope was never that of class D in the first place. The scope of x ended when class D closed because x is neither an instance variable nor a class variable.
I hope that helps.
A part of the reason of why this behavior makes sense lies with metaprogramming capabilities; you could be using some temporary variables to store data that you could use to name a new constant, or to reference a method name:
class A
names, values = get_some_names_and_values()
names.each_with_index do |name, idx|
const_set name, value[idx]
end
end
Or maybe, you need to get the eigenclass...
class B
eigenclass = class << self; self; end
eigenclass.class_eval do
...
end
end
It make sense to have a new scope every time you reopen a class, because in Ruby you often write code inside a class definiton that is actual code to be executed and opening a class is just a way to get the right value for self. You don't want the content of the class to be polluted by these variables, otherwhise you'd use a class instance variable:
class C
#answer = 42
end
class C
p #answer
end

metaprogramming access local variables

class Foo
def initialize
bar = 10
end
fiz = 5
end
Is there a possibility to get these local values (outside the class) ?
The local variable in initialize would be lost.
You are able to get the value fiz outside of the class, but only upon defining that class, and recording the return of the definition of the class.
return_of_class_definition = (class A ; fiz = 5 ; end) would assign the value of fiz to the variable.
You can also use binding but of course, this means changing the class, which may not be allowed for the exercise.
class A
bin = 15
$binding = binding
end
p eval 'bin', $binding
No. Once a local variable goes out of scope (for bar that is when the initialize method has run - for fiz when the end of the class definition has been reached), it's gone. No trace left.
While a local variable is still in scope you can see it (well, its name) with local_variables and get and set its value with eval (though that's definitely not recommended for sanity reasons), but once it's out of scope, that's it. No way to get it back.
In ruby we have something we could call scope gates - places when a program written in ruby leaves the previous scope. Those gates are: class, module and method (def keyword). In other words after class, module of def keyword in the code you're immediately entering into a new scope.
In ruby nested visibility doesn't happen and as soon as you create a new scope, the previous binding will be replaced with a new set of bindings.
For example if you define following class:
x = 1
class MyClass
# you can't access to x from here
def foo
# ...from here too
y = 1
local_variables
end
end
local_variables method call will return [:y]. It means that we don't have an access to the x variable. You can workaround this issue using ruby's technique called Flat Scopes. Basically instead defining a class using class keyword you can define it using Class.new and pass a block to this call. Obviously a block can take any local variables from the scope where it was defined since it's a closure!
Our previous example could be rewritten to something like like that:
x = 1
Foo = Class.new do
define_method :foo do
i_can_do_something_with(x)
y = 1
local_variables
end
end
In this case local_variables will return [:x, :y].

Resources