Scopes and blocks - ruby

In ruby, module, class, and def keywords define a new scope. I'm confused as to why local variables defined in a block do not exist outside of the block. Is a block argument another scope gate? For example:
(1..2).each { |n| numer = 'czesc' }
numer # => NameError: undefined local variable or method `czesc' for main:Object
Or more simply:
def kon; end
kon { kot = 3 }
kot # => NameError: undefined local variable or method `kot' for main:Object
I thought, maybe it's not persisted because it's defined in the method's arguments, but the following works for the normal arguments:
def pokaz(cos)
p cos
end
pokaz(co = "to")
co # => "to"

You might think about codeblocks as of lazy instances of Proc class.
Your second example with kon/kot does actually not what you were expecting from it. You supply a codeblock to a function kon. This codeblock is not evaluated unless requested. In your snippet it is never evaluated. Look:
▶ def kon; end
#⇒ :kon
▶ kon { puts 'I am here' }
#⇒ nil
You passed a codeblock. Fine. Now kon should invoke it, if needed:
▶ def kon; yield ; end
#⇒ :kon
▶ kon { puts 'I am here' }
# I am here
#⇒ nil
When you passed a codeblock to, say, each method of Enumerator instance:
(1..2).each { |n| numer = 'czesc' }
The codeblock is being evaluated in context of this Enumerator. The default receiver, self, is still the main thread, though. That makes codeblocks to act mostly like closures (they have an access to the caller bindings):
▶ kot = 10
#⇒ 10
▶ 5.times do |i|
▷ kot = i
▷ end
▶ puts kot
#⇒ 4
Hope it sheds some light.

In ruby, module, class, and def keywords define a new scope.
There are six scope constructs in Ruby: module bodies, class bodies, method bodies and script bodies create new scopes, block bodies and "stabby lambda" literal bodies create new nested scopes.
I'm confused as to why local variables defined in a block do not exist outside of the block.
Because a block body has its own lexical scope. The scope is nested, which means that it has access to local variables from the outer scope, but variables from an inner scope never leak into the outer scope.

Related

Ruby local variable scope

I'm struggling with variable scope in ruby. I was under the impression that local variables were accessible by methods below them. Looking at the following code however I'm getting an undefined variable error.
a = Array.new
def testing()
a.push("test")
end
testing
Using global variables it works just fine, how can I avoid using global variables?
There isn't much to say here except that local variables in Ruby are only accessible in the scope in which they are defined and any blocks (closures) defined in that scope that capture them. Since in Ruby, unlike some other dynamic languages, method are not closures, they do not capture local variables.
If you tried, say, this:
irb(main):001:0> a = 3
=> 3
irb(main):002:0> define_method(:testing) do
irb(main):003:1* puts a
irb(main):004:1> end
=> :testing
irb(main):005:0> testing
3
It works, since the code is in a block instead of a method.
Defining a method in the top-level can be quite confusing. Let's wrap your code in a class instead:
class Foo
a = []
def testing
a << 'test'
end
end
(I've shortened Array.new to [] and a.push(...) to a << ...)
Foo#testing can be called via:
foo = Foo.new
foo.testing
#=> undefined local variable or method `a'
Apparently, this doesn't work. The first a is a local variable in the scope of the class body, whereas the second a is a local variable within an instance method.
Moving the variable initialization out of the class body into the initialize method doesn't work either, because local variables are not shared across methods:
class Foo
def initialize
a = [] # <- one 'a'
end
def testing
a << 'test' # <- another 'a'
end
end
To get this working, you have to use an instance variable:
class Foo
def initialize
#a = []
end
def testing
#a << 'test'
end
end
foo = Foo.new
foo.testing
#=> ["test"]
foo.testing
#=> ["test", "test"]
You could use instance variables. Any variable whose name begins with # is an instance variable and is available anywhere in the class or method in which it is defined. For example, the variable #A defined within class B will be available to any methods in B.
2.3.3 :007 > def testing()
2.3.3 :008?> [].push("test")
2.3.3 :009?> end
=> :testing
2.3.3 :010 > testing
=> ["test"]
You can't let local variables accessible by methods below them , you can use block like the answer by #Linuxios, or use the way that it easy work.

Changing the binding of a block

I am trying to write a class that can capture a block of code and subsequently change the variables in the scope/closure.
class BlockCapture
attr_reader :block
def initialize(&block)
#block = block
#scope_additions = {}
end
def merge_into_scope(values = {})
#scope_additions = #scope_additions.merge(values)
end
def mutated_block
# some code that merges the scope additions into the binding
# and returns the context
end
end
captured_block = BlockCapture.new do
future_variable.upcase
end
captured_block.block.call # This should trigger a NameError, as `future_variable` is not in scope.
captured_block.merge_into_scope(future_variable: "I was added to the scope")
captured_block.mutated_block.call # This should work
=> "I WAS ADDED TO THE SCOPE"
I don't know how to go about merging the variables into the scope.
I have tried calling the binding method on the block variable, but it returns a new binding each time, so it appears to be a copy of rather than a reference to the binding of the original block.
irb(main):078:0> capture.block.binding
=> #<Binding:0x007fa38292e398>
irb(main):079:0> capture.block.binding
=> #<Binding:0x007fa382925f18>
irb(main):080:0> capture.block.binding
=> #<Binding:0x007fa38291d908>
Positing as an answer, even if I am not sure it would be helpful, because there is no space in the comment. Looks like you can run eval with the specific binding, for example with the current binding, created after the lambda was defined:
bind_lambda = -> (bind, x) { bind.eval x }
#=> #<Proc:0x007fec943b85e8#(pry):1 (lambda)>
First try ends with an error, as expected:
bind_lambda.call binding, 'future_var'
NameError: undefined local variable or method `future_var' for main:Object
from (pry):2:in `__pry__'
But after declaration of the local variable:
future_var = "here be dragons"
#=> "here be dragons"
We can reach it:
bind_lambda.call binding, 'future_var'
#=> "here be dragons"
This is because binding is the current environment.

Why do instance variables seemingly disappear when inside a block?

Forgive me, guys. I am at best a novice when it comes to Ruby. I'm just curious to know the explanation for what seems like pretty odd behavior to me.
I'm using the Savon library to interact with a SOAP service in my Ruby app. What I noticed is that the following code (in a class I've written to handle this interaction) seems to pass empty values where I expect the values of member fields to go:
create_session_response = client.request "createSession" do
soap.body = {
:user => #user, # This ends up being empty in the SOAP request,
:pass => #pass # as does this.
}
end
This is despite the fact that both #user and #pass have been initialized as non-empty strings.
When I change the code to use locals instead, it works the way I expect:
user = #user
pass = #pass
create_session_response = client.request "createSession" do
soap.body = {
:user => user, # Now this has the value I expect in the SOAP request,
:pass => pass # and this does too.
}
end
I'm guessing this strange (to me) behavior must have something to do with the fact that I'm inside a block; but really, I have no clue. Could someone enlighten me on this one?
First off, #user is not a "private variable" in Ruby; it is an instance variable. Instance variables are available within the the scope of the current object (what self refers to). I have edited the title of your question to more accurately reflect your question.
A block is like a function, a set of code to be executed at a later date. Often that block will be executed in the scope where the block was defined, but it is also possible to evaluate the block in another context:
class Foo
def initialize( bar )
# Save the value as an instance variable
#bar = bar
end
def unchanged1
yield if block_given? # call the block with its original scope
end
def unchanged2( &block )
block.call # another way to do it
end
def changeself( &block )
# run the block in the scope of self
self.instance_eval &block
end
end
#bar = 17
f = Foo.new( 42 )
f.unchanged1{ p #bar } #=> 17
f.unchanged2{ p #bar } #=> 17
f.changeself{ p #bar } #=> 42
So either you are defining the block outside the scope where #user is set, or else the implementation of client.request causes the block to be evaluated in another scope later on. You could find out by writing:
client.request("createSession"){ p [self.class,self] }
to gain some insight into what sort of object is the current self in your block.
The reason they "disappear" in your case—instead of throwing an error—is that Ruby permissively allows you to ask for the value of any instance variable, even if the value has never been set for the current object. If the variable has never been set, you'll just get back nil (and a warning, if you have them enabled):
$ ruby -e "p #foo"
nil
$ ruby -we "p #foo"
-e:1: warning: instance variable #foo not initialized
nil
As you found, blocks are also closures. This means that when they run they have access to local variables defined in the same scope as the block is defined. This is why your second set of code worked as desired. Closures are one excellent way to latch onto a value for use later on, for example in a callback.
Continuing the code example above, you can see that the local variable is available regardless of the scope in which the block is evaluated, and takes precedence over same-named methods in that scope (unless you provide an explicit receiver):
class Foo
def x
123
end
end
x = 99
f.changeself{ p x } #=> 99
f.unchanged1{ p x } #=> 99
f.changeself{ p self.x } #=> 123
f.unchanged1{ p self.x } #=> Error: undefined method `x' for main:Object
From the documentation:
Savon::Client.new accepts a block inside which you can access local variables and even public methods from your own class, but instance variables won’t work. If you want to know why that is, I’d recommend reading about instance_eval with delegation.
Possibly not as well documented when this question was asked.
In the first case, self evaluates to client.request('createSession'), which doesn't have these instance variables.
In the second, the variables are brought into the block as part of the closure.
Another way to fix the issue would be to carry a reference to your object into the block rather than enumerating each needed attribute more than once:
o = self
create_session_response = client.request "createSession" do
soap.body = {
:user => o.user,
:pass => o.pass
}
end
But now you need attribute accessors.

Defining a method that uses an out-of-scope variable in Ruby

I want to make a Test::Unit test_helper method that I can call to wipe a bunch of tables after the tests execute. Here's the general idea I have:
def self.wipe_models(*models)
def teardown
models.each do |model|
model = model.to_s.camelize.constantize
model.connection.execute "delete from #{model.table_name}"
end
end
end
However, when teardown runs, I get:
undefined local variable or method `models'
To me it looks like the "def" block doesn't obey usual rules for closures; I can't access variables defined outside of its scope.
So, how do I access a variable that's defined outside of a "def" method declaration?
You can do it as a closure with define_method:
def self.wipe_models(*models)
define_method(:teardown) do
models.each do |model|
model = model.to_s.camelize.constantize
model.connection.execute "delete from #{model.table_name}"
end
end
end
Now the method body is a block and can access models.
Method definitions are not closures in Ruby. The class, module, def, and end keywords are all scope gates. In order to maintain scope across a scope gate you have to pass a block; blocks are closures and thus run in the scope in which they were defined.
def foo
# since we're in a different scope than the one the block is defined in,
# setting x here will not affect the result of the yield
x = 900
puts yield #=> outputs "16"
end
# x and the block passed to Proc.new have the same scope
x = 4
square_x = Proc.new { x * x }
foo(&square_x)
Use a class instance variable:
cattr_accessor :models_to_wipe
def self.wipe_models(*models)
self.models_to_wipe = models
end
def teardown
self.class.models_to_wipe.each do |model|
model = model.to_s.camelize.constantize
model.connection.execute "delete from #{model.table_name}"
end
end

Ruby: convert proc to lambda?

Is it possible to convert a proc-flavored Proc into a lambda-flavored Proc?
Bit surprised that this doesn't work, at least in 1.9.2:
my_proc = proc {|x| x}
my_lambda = lambda &p
my_lambda.lambda? # => false!
This one was a bit tricky to track down. Looking at the docs for Proc#lambda? for 1.9, there's a fairly lengthy discussion about the difference between procs and lamdbas.
What it comes down to is that a lambda enforces the correct number of arguments, and a proc doesn't. And from that documentation, about the only way to convert a proc into a lambda is shown in this example:
define_method always defines a method without the tricks, even if a non-lambda Proc object is given. This is the only exception which the tricks are not preserved.
class C
define_method(:e, &proc {})
end
C.new.e(1,2) => ArgumentError
C.new.method(:e).to_proc.lambda? => true
If you want to avoid polluting any class, you can just define a singleton method on an anonymous object in order to coerce a proc to a lambda:
def convert_to_lambda &block
obj = Object.new
obj.define_singleton_method(:_, &block)
return obj.method(:_).to_proc
end
p = Proc.new {}
puts p.lambda? # false
puts(convert_to_lambda(&p).lambda?) # true
puts(convert_to_lambda(&(lambda {})).lambda?) # true
It is not possible to convert a proc to a lambda without trouble. The answer by Mark Rushakoff doesn't preserve the value of self in the block, because self becomes Object.new. The answer by Pawel Tomulik can't work with Ruby 2.1, because define_singleton_method now returns a Symbol, so to_lambda2 returns :_.to_proc.
My answer is also wrong:
def convert_to_lambda &block
obj = block.binding.eval('self')
Module.new.module_exec do
define_method(:_, &block)
instance_method(:_).bind(obj).to_proc
end
end
It preserves the value of self in the block:
p = 42.instance_exec { proc { self }}
puts p.lambda? # false
puts p.call # 42
q = convert_to_lambda &p
puts q.lambda? # true
puts q.call # 42
But it fails with instance_exec:
puts 66.instance_exec &p # 66
puts 66.instance_exec &q # 42, should be 66
I must use block.binding.eval('self') to find the correct object. I put my method in an anonymous module, so it never pollutes any class. Then I bind my method to the correct object. This works though the object never included the module! The bound method makes a lambda.
66.instance_exec &q fails because q is secretly a method bound to 42, and instance_exec can't rebind the method. One might fix this by extending q to expose the unbound method, and redefining instance_exec to bind the unbound method to a different object. Even so, module_exec and class_exec would still fail.
class Array
$p = proc { def greet; puts "Hi!"; end }
end
$q = convert_to_lambda &$p
Hash.class_exec &$q
{}.greet # undefined method `greet' for {}:Hash (NoMethodError)
The problem is that Hash.class_exec &$q defines Array#greet and not Hash#greet. (Though $q is secretly a method of an anonymous module, it still defines methods in Array, not in the anonymous module.) With the original proc, Hash.class_exec &$p would define Hash#greet. I conclude that convert_to_lambda is wrong because it doesn't work with class_exec.
Here is possible solution:
class Proc
def to_lambda
return self if lambda?
# Save local reference to self so we can use it in module_exec/lambda scopes
source_proc = self
# Convert proc to unbound method
unbound_method = Module.new.module_exec do
instance_method( define_method( :_proc_call, &source_proc ))
end
# Return lambda which binds our unbound method to correct receiver and calls it with given args/block
lambda do |*args, &block|
# If binding doesn't changed (eg. lambda_obj.call) then bind method to original proc binding,
# otherwise bind to current binding (eg. instance_exec(&lambda_obj)).
unbound_method.bind( self == source_proc ? source_proc.receiver : self ).call( *args, &block )
end
end
def receiver
binding.eval( "self" )
end
end
p1 = Proc.new { puts "self = #{self.inspect}" }
l1 = p1.to_lambda
p1.call #=> self = main
l1.call #=> self = main
p1.call( 42 ) #=> self = main
l1.call( 42 ) #=> ArgumentError: wrong number of arguments (1 for 0)
42.instance_exec( &p1 ) #=> self = 42
42.instance_exec( &l1 ) #=> self = 42
p2 = Proc.new { return "foo" }
l2 = p2.to_lambda
p2.call #=> LocalJumpError: unexpected return
l2.call #=> "foo"
Should work on Ruby 2.1+
Cross ruby compatiable library for converting procs to lambdas:
https://github.com/schneems/proc_to_lambda
The Gem:
http://rubygems.org/gems/proc_to_lambda
The above code doesn't play nicely with instance_exec but I think there is simple fix for that. Here I have an example which illustrates the issue and solution:
# /tmp/test.rb
def to_lambda1(&block)
obj = Object.new
obj.define_singleton_method(:_,&block)
obj.method(:_).to_proc
end
def to_lambda2(&block)
Object.new.define_singleton_method(:_,&block).to_proc
end
l1 = to_lambda1 do
print "to_lambda1: #{self.class.name}\n"
end
print "l1.lambda?: #{l1.lambda?}\n"
l2 = to_lambda2 do
print "to_lambda2: #{self.class.name}\n"
end
print "l2.lambda?: #{l2.lambda?}\n"
class A; end
A.new.instance_exec &l1
A.new.instance_exec &l2
to_lambda1 is basically the implementation proposed by Mark, to_lambda2 is a "fixed" code.
The output from above script is:
l1.lambda?: true
l2.lambda?: true
to_lambda1: Object
to_lambda2: A
In fact I'd expect instance_exec to output A, not Object (instance_exec should change binding). I don't know why this work differently, but I suppose that define_singleton_method returns a method that is not yet bound to Object and Object#method returns an already bound method.

Resources