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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With