Why can a method definition without a block parameter accept a block? - ruby

Why can a method definition without a block parameter accept a block? This is the demo code:
def fun
yield
end
fun {puts 'hello ruby'}

Because that's how ruby works. Any method can be passed a block. It is responsibility of that method to check if block_given? and yield to it if needed.
This is implicit block passing. When you declare a block parameter, then something different happens: the block is converted to a Proc object, so that it can be called like a function and passed around as a parameter. You can't do that with implicit blocks (AFAIK).
def foo &block
block.call 3
bar block
end
# this method expects proc as a regular parameter (not a block), so you can pass
# a block in addition to it (if you so desire)
def bar block
block.call 4
end
foo do |x|
puts "this is #{x}"
end
# >> this is 3
# >> this is 4

Related

Don't pass block when calling super

How can I set a block to nil for the super call?
class A
def foo
if block_given?
result = yield
# do stuff with the yield result
end
# some more code
end
end
class B < A
def foo
block_result = yield
# process block results and store it
# ...
super
end
end
B.new.foo { puts "block called" }
# => block called
# => block called
I don't want to yield the block twice. Is it somehow possible that block_given? in class A returns false?
Background is that I don't own the A class and I can't change it's foo method but I want to avoid calling my block twice. I also don't want to pass a dummy / empty block to super, because the behaviour of A's foo method changes when a block is given.
It's actually not that obvious. From the documentation we know that:
calling super passes all arguments
calling super() passes no arguments
What the documentation doesn't say is that this only applies to positional and keyword arguments. super() still passes a given block!
You have to explicitly unset the block by calling:
super(&nil)
How does this work?
You probably know that you can define a method with an explicit block argument:
def foo(&block)
# ...
end
and that you can pass it as a block to another method: (that other method might be super)
def foo(&block)
super(&block)
end
Now, if you pass a block when calling the method, the corresponding block variable will be an instance of Proc:
def foo(&block)
p block_given: block_given?, block: block
end
foo {}
#=> {:block_given=>true, :block=>#<Proc:0x00007ff4990d0030>}
If you call it without passing block, the block variable will just be nil:
foo
#=> {:block_given=>false, :block=>nil}
So if no block is given, block is nil and
super(&block)
becomes:
super(&nil)

What does this '&' mean in ruby? [duplicate]

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.

Can I pass a block to a Proc?

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.

Can I pass a block which itself expect a block to instance_exec in ruby?

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.

ArgumentError calling a Proc

s = Proc.new {|x|x*2}
puts s.call(5)
-> 10
def foo(&a)
a.call(5)
end
puts "test foo:"
foo(s)
When I try to call the proc above, I get:
foo: wrong number of arguments (1 for 0) (ArgumentError)
My expectation was that I can pass a proc to a method if the method is defined with this type of signature:
def foo(&a)
and then I can execute the proc insiide foo like this:
a.call(5)
If you want to pass an actual proc to foo, just define it with def foo(a). Putting the & in front of a means "this isn't actually an argument. Instead take the block passed to this method, create a proc out of it, and store that proc in the variable a". In other words with your current definition you can call foo like this:
foo do |x|
puts x*2
end
You can also use & when calling a method to turn a proc into a block:
foo(&s)

Resources