Scope is a program space within which certain types of variables are visible.
Why is Global State evil? Main reasons:
- it is hard to track values and assignments of variables throughout the program
- since there is only one namespace available, naming variables, which is hard, becomes even harder
Following are types of variables in Ruby:
- Global variables - $foo Available from anywhere.
- Class variables - @@foo
- available within class they are defined
- available within subclasses of that class
- also available within instances of the class in which they are defined, and in instances of their subclasses
- Instance variables - @foo Available only in an instances of their class.
- Local variables - foo_bar Depends on the scope in which they are defined in.
-
Instance variables do not need an explicit assignment when they are defined. nil gets assigned to them by default. In contrast, local variables must have an explicit assignment.
@foo # => nil bar # => what is the name of the error that gets raised? [1] _________ #if false #foobar = "foobar" #end p foobar # What happens when program runs with 'false' in the if statement? [2] # What happens when we remove the if statement? [3] # What has precedence, method or a variable? [4] def boo :boo end boo = :BOO p boo # Is it possible to get to the method in this case? [5.1] # How? [5.2]
- in contrast to local variables, get binded to an object and not to a certain scope
- they change with each new object, local variables with each new scope
- are "lines" which create new scope, and are invoked with keywords:
- class
- module
- def
- each time program encounters one of those keywords, new scope gets created
foo = :foo
p local_variables [6.1]
class A
bar = :bar
p local_variables [6.2]
def foobar
foobar = :foobar
p local_variables [6.3]
end
end
A.new.foobar
p local_variables [6.4]
# What is the output of each call to 'local_variables'?
- blocks are not Scope Gates, but they do allow creation of local variables defined within them
a = [1, 2, 3]
a.each do |n; hello|
p n
hi = "hi"
end
p hi # => What will this variable reference result in? [7]
- block variables can be used to shadow outer variables
foo = :foo
[1, 2, 3].each { |n; foo| p n, foo }
# What is the value of 'foo' within the block? [8]
p foo
- new block scope gets created on each iteration
2.times do
foo ||= 5
p foo
foo *= 2
p foo
end
# => What is the output? [9]
- behaviour with procs
def foo
bar = :bar
-> { bar }
end
bar = :BAR
p foo.call
# => Output? [10]
boo = 1
l = -> { boo }
boo = 2
3
p l.call
# Output? [11]
Answers:
- [1] - NameError
- [2] - it gets assigned to 'nil'
- [3] - gets set to 'foobar'
- [4] - variables > methods
- [5.1] - yes
- [5.2] - using parentheses
- [6.1] - [:foo]
- [6.2] - [:bar]
- [6.3] - [:foobar]
- [6.4] - [:foo]
- [7] - NameError: undefined local variable ...
- [8] - nil
- [9] - 1st run '5 10', 2nd run '5 10' (because new scope gets created on each iteration)
- [10] - :bar
- [11] - 2
$alpha = "alpha"
class Foo
$gama = "gama"
@@delta = "delta"
def foo_first
p $alpha
end
def foo_second
$beta = "beta"
end
end
class Bar < Foo
attr_reader :zeta
p $gama # (1)
def initialize
@zeta = "zeta"
end
def bar_first
p @@delta
end
def bar_second
p @@epsilon
end
end
class Doo < Bar
attr_reader :zeta
eta = "eta"
def self.eta
p eta
eta = "ETA"
p eta
end
def self.eta_2
eta = "ETA 2"
p eta
end
def doo_first
@@epsilon = "epsilon"
end
end
# Global variables
f = Foo.new
f.foo_first # (2)
p $beta # (3)
f.foo_second
p $beta # (4)
# Class variables
b = Bar.new
b.bar_first # (5)
d = Doo.new
d.doo_first
b.bar_second # (6)
# Instance variables
p b.zeta # (7)
p d.zeta # (8)
# Local variables
Doo.eta # (9)
Doo.eta_2 # (10)
Answers:
- 1 - "gama"
- 2 - "alpha"
- 3 - nil
- 4 - "beta"
- 5 - "delta"
- 6 - NameError
- 7 - "zeta"
- 8 - nil
- 9 - SystemStackError
- 10 - "ETA 2"