I'm wondering if it's possible to pass a block to a Proc. Simply passing a block to Proc.call doesn't work:
foo = Proc.new {
yield
}
foo.call {
puts "test"
}
Results in:
LocalJumpError: no block given (yield)
The same happens with lambdas. However this does work with method objects:
class Foo
def bar
yield
end
end
bar = Foo.new.method :bar
bar.call { puts "Success!" }
Results in:
Success!
The odd thing is that it still works after converting the method object into a proc:
bar.to_proc.call { puts "Success!" }
Results in:
Success!
So how come the Proc that was made from a block doesn't accept blocks, but the Proc that was originally a method does? Is it possible to create Procs from blocks that accepts blocks?
Procs can't accept blocks as implicit arguments (the format you're trying). A proc can receive other proc objects as arguments, either explicitly, or using & arguments. Example:
a = Proc.new do |&block|
block.call
end
a.call() {puts "hi"}
yield is a bit of laguage level magic that only works in the context of a method.
The above answer is not 100% correct therefore can't be accepted answer. Especially the part;
Procs can't accept blocks as implicit arguments (the format you're trying). A proc can receive other proc objects as arguments, either explicitly, or using & arguments.
This is wrong. Procs and lambdas can call yield in their bodies. The fact to keep in mind is, Proc/lambda bodies have a lexical scope! Which means, if there is a block while defining the Proc/lambda, yield would successfully execute, like so;
def foo
my_proc = Proc.new { yield }
my_proc.call
end
foo { puts "Hello world!" } # would print "Hello world!"
As you can see, yield worked! Because there was block while defining the Proc.
One can say, the Proc is unfolded into method which has block while calling therefore yield worked. This is also wrong and can be disproved easily with the following snippet;
def foo
#my_proc ||= Proc.new { yield }
#my_proc.call
end
foo { puts "Hello again!" } # would print "Hello world!"
foo # would print "Hello world!"
As you can again see, it's about having block while defining the Proc.
If you want to have better understanding of whats being lexically scoped mean, let's have a look at the following example.
class Foo
def self.hello_proc
Proc.new { puts name }
end
def self.name
"Alice"
end
end
class Bar
def self.put_name
Foo.hello_proc.call
end
def self.name
"Bob"
end
end
Bar.put_name # would print "Alice"
You can copy and paste above code to an irb session to see what is the output. The reason it puts "Alice" is, the name was "Alice" while the Proc's being defined.
Related
Why is Proc in ruby return before executing remaining codes in a method from which Proc was called?
def hello
a = Proc.new{ return }
a.call
puts "Hello"
end
def proc
hello
puts "Proc"
end
Here return will skip puts "Hello" and prints only puts "Proc"
But lambda prints puts "Hello" as well.
What's the reason for this?
You should see comment in this answer https://stackoverflow.com/a/723/4576274.
It states
A lambda is an anonymous method. Since it's a method, it returns a
value, and the method that called it can do with it whatever it wants,
including ignoring it and returning a different value.
A Proc is like
pasting in a code snippet. It doesn't act like a method. So when a
return happens within the Proc, that's just part of the code of the
method that called it
This question already has answers here:
What does map(&:name) mean in Ruby?
(17 answers)
Closed 6 years ago.
https://github.com/activeadmin/activeadmin/blob/1c85c5654a2ce1d43d4c64d98b928ff133d46406/lib/active_admin.rb#L95
What does the & (prefixed to ActiveAdmin) in this code mean?
def before_load(&block)
ActiveSupport::Notifications.subscribe(
ActiveAdmin::Application::BeforeLoadEvent,
&ActiveAdmin::Event.wrap_block_for_active_support_notifications(block)
)
end
In functions & 'splats' blocks (also calls to_proc on it), similar to * for regular parameters.
That is somewhat equivalent to
def before_load(&block) # this binds provided block into block variable
ActiveSupport::Notifications.subscribe(ActiveAdmin::Application::BeforeLoadEvent){|some_params_maybe|
ActiveAdmin::Event.wrap_block_for_active_support_notifications(block).call(some_params_maybe)
}
end
usually & calls to_proc method of the object, such as gets.split.map(&:to_i) is used to read line of integers, which is the same as map { |e| e.to_i }
In method arguments, it appears in the last arguments, meaning the code block you passed to, check the following code for details.
def g
puts yield "g"
end
def h(block)
puts block.call "h"
end
def f(&block)
puts block.class # => Proc
puts block.call "f" # => hello f
g &block # => hello g passed as code block
h block # => hello h passed as proc
end
f { |x| "hello " + x }
Following article provides a good explanation on the use of '&' in Ruby:
The Implicit Block
Methods in Ruby can take arguments in all sorts of interesting ways. One case that’s especially interesting is when a Ruby method takes a block.
In fact, all Ruby methods can implicitly take a block, without needing to specify this in the parameter list or having to use the block within the method body e.g.:
def hello
end
hello do
puts "hello"
end
This will execute without any trouble but nothing will be printed out as we’re not executing the block that we’re passing in. We can – of course – easily execute the block by yielding to it:
def hello
yield if block_given?
end
hello do
puts "hello"
end
This time we get some output:
hello
We yielded to the block inside the method, but the fact that the method takes a block is still implicit.
It gets even more interesting since Ruby allows to pass any object to a method and have the method attempt to use this object as its block. If we put an ampersand in front of the last parameter to a method, Ruby will try to treat this parameter as the method’s block. If the parameter is already a Proc object, Ruby will simply associate it with the method as its block.
def hello
yield if block_given?
end
blah = -> {puts "lambda"}
hello(&blah)
lambda
If the parameter is not a Proc, Ruby will try to convert it into one (by calling to_proc on it) before associating it with the method as its block.
def hello
yield if block_given?
end
class FooBar
def to_proc
-> {puts 'converted lambda'}
end
end
hello(&FooBar.new)
converted lambda
All of this seems pretty clear, but what if I want to take a block that was associated with a method and pass it to another method? We need a way to refer to our block.
The Explicit Block
When we write our method definition, we can explicitly state that we expect this method to possibly take a block. Confusingly, Ruby uses the ampersand for this as well:
def hello(&block)
yield if block_given?
end
hello do
puts "hello"
end
Defining our method this way, gives us a name by which we can refer to our block within the method body. And since our block is a Proc object, instead of yielding to it, we can call it:
def hello(&block)
block.call if block_given?
end
hello do
puts "hello"
end
I prefer block.call instead of yield, it makes things clearer. Of course, when we define our method we don’t have to use the name ‘block’, we can do:
def hello(&foo)
foo.call if block_given?
end
hello do
puts "hello"
end
Having said that; ‘block’ is a good convention.
I have two methods which both except a block, however one of the methods needs to pass its block to the other.
def one(&block)
two(block)
end
def two(&block)
block.call
end
In the real code other parameters are passed and one is syntax sugar over two.
I want to be able to call both one and two with a block.
one { } # => okay
two { } # => ArgumentError: wrong number of arguments (1 for 0)
I can see why I get the ArgumentError, two takes no argument as such. I'm not quite sure how to phrase this but &block designates the block appears after the passed in arguments, hence the error.
Ruby 1.9
You could just pass the &block to your second method like so:
def one(&block)
two(&block)
end
def two(&block)
block.call
end
one { puts "Hello World" }
Hello World
#=> nil
Update
You could also do something like this
def one
two(&Proc.new)
end
def two(&block)
block.call
end
It will have the same output as above. Do note that if no block is given to one, it will raise an ArgumentError: tried to create Proc object without a block so you'd have to check if the block is given by calling if block_given?
You could do the same with yield:
def one
two { yield }
end
def two
yield
end
I expect the code
foo=proc{puts "foo"}
instance_exec(1,2,3,&foo) do |*args , &block|
puts *args
block.call
puts "bar"
end
to output
1
2
3
foo
bar
But got the error
both block arg and actual block given
Can I pass a block which itself expect a block to instance_exec in ruby?
&foo tries to pass foo as a block to instance_exec, and you are already passing an explicit block. Omitting ampersand sends foo just like any other argument (except that it is a Proc instance). So, try this instead:
instance_exec(1,2,3,foo) do |*args, block|
puts *args
block.call
puts "bar"
end
This also means that you can do something like:
bar = proc{ |*args, block|
puts *args
block.call
puts "bar"
}
instance_exec(1,2,3,foo,&bar)
And get the same result.
More info at Difference between block and &block in Ruby
I'm about 3 years late to this party, but I thought I'd share an approach that let's you treat the inner block more like a real block, rather than just a plain old argument.
The best way I know of to go about this is to create an object to act as a binding context and define the outer block as a method. So if I rewrite the original example as follows without the instance_exec call...
inner_proc = proc { puts "inner" }
outer_proc = proc { |*args, &inner_block|
puts *args
inner_block.call
puts "bar"
}
We can define outer_proc as a method on an object
scope_object = Object.new
scope_object.define_singleton_method :bound_proc, &outer_proc
Now you can call scope_object.bound_proc instead of the instance_exec call above.
scope_object.bound_proc 1, 2, 3, &inner_proc
You'll get:
1
2
3
inner
bar
Unfortunately, you'll get a LocalJumpError if you try to yield inside of outer_proc, rather than the inner_block.call, I'm not entirely sure why. If someone has that answer then I'd be interested.
I want to pass a block to a block that is instance_eval-ed like this,
instance_eval(&block) { puts "test" }
where block is defined as containing something like:
puts "Incoming message:"
yield
Is this possible? I discovered a way of doing this with fibers, but I'm trying to use yield first. Looking at this question, it looks like this might not be possible, but I wanted to confirm.
It's weird, indeed. Why instance_eval ? It's usually used to change self, and evaluate in the context of the receiver.
cat = String.new('weird cat')
block1 = lambda do |obj, block|
puts "Incoming message for #{obj}:"
block.call
end
block2 = Proc.new { puts "test" }
block3 = lambda {|obj| block1.call(obj, block2)}
cat.instance_eval(&block3)
Execution (Ruby 1.9.2) :
$ ruby -w t2.rb
Incoming message for weird cat:
test