metaprogramming access local variables - ruby

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

Related

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.

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

Instance variables through methods

In the following example everything is logical for me:
class Something
def initialize
#x=101
end
def getX
return #x
end
end
obj = Something.new
puts obj.getX
=>101
Something.new will create new instance of class Something with instance variable #x which will be visible to all methods of class Something.
But what will happen in second example without initialize(constructor) method.
class Something
def bla_bla_method
#x=101
end
def getX
return #x
end
end
obj = Something.new
puts obj.getX
=>nil
obj.bla_bla_method
puts obj.getX
=>101
So now bla_bla_method when called will create(like constructor) new instance_variable #x and will add that instance variable in "instance variable table" which is available to all methods again?
So now if i add new method "new_method" in class Something(in second example):
def new_method
#x=201
end
...
obj.bla_bla_method
puts obj.getX
=>101
obj.new_method
puts obj.getX
=>201
So if im getting this right, every method of class can create new instance variable which is available to all methods of class? And then every method can overwrite that instance variable over and over again(cyclical)?
I'm new to ruby so maybe here i'm doing big mistake or don't understand some basic concept , so dont yell :D
Instance variables for an object can be named and manipulated while the object exists. See the example below, when we are using the irb prompt object:
$ irb
> instance_variables # => [:#prompt]
> #foo # => nil
> instance_variables # => [:#prompt]
> #foo = 1 # => 1
> instance_variables # => [:#prompt, :#foo]
> #foo # => 1
Now, here's a description of Class#new from the docs:
Calls allocate to create a new object of class’s class, then invokes that object’s initialize method, passing it args. This is the method that ends up getting called whenever an object is constructed using .new.
One way to think of this is that initialize is functionally a regular method just like your other instance methods, only it gets called by Class#new to provide us with an easy way of setting default values (among others).
Technically, yes. But consider the notion of Object Oriented programming - Creating real world abstractions in form of classes and objects.
For instance, if you are talking about a student in a school; you know that is an abstractable entity. So you go ahead and encapsulate the common characteristic of student in a class Student.
initialize is a constructor. When you create a new student in your system, you would naturally want to supply few necessary details about him like his name, age and class.
So in initialize method you set those instance variables.
Few students also study in school; so naturally they will acquire some grade and stuff; to instantiate those details about the student, you would want to do that with something like this:
#Student(name, age, class)
kiddorails = Student.new("Kiddorails", 7, 2)
#to grade:
kiddorails.set_grades
#do stuff
So you can mutate and set the instance variables in an object, almost anywhere you want in a class; but the point is - Do it, where it makes sense.
PS: You should always set default values to the instance variables which are not set via initialize in initialize, if needed.

How Do I Make This Code Set the Value of the Instance Variable?

I have the following code, meant to turn a normal variable into an instance variable.
BasicObject.class_eval do
def instance(ins)
self.instance_variable_set("##{ins}", ins)
end
end
Lets say the user says
class X
foo = 3
end
bar = X.new
bar.instance(:foo)
What I want it to do is set the new created variable, #foo to 3, instead, it sets #foo to :foo. How do I make the code do what I want?
Inside your instance method, the parameter ins contains the name of the variable, not its value. As written, there's no way to get at its value from that point.
If you call instance from a point in the code where the source variable is actually visible, which it's not in your example (see my comment above), you can also pass the local variable binding, and then use that. Something like this:
def instance(var, bound)
eval "##{var}=#{var}", bound
end
Which would work like this:
foo = 3
instance('foo', binding)
#foo # => 3

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

Resources