The question is inspired by this one.
Proc::new
has an option to be called without a block inside a method:
Proc::new
may be called without a block only within a method with an attached block, in which case that block is converted to theProc
object.
When the proc
/lambda
instance is passed as a code block, the new instance of Proc
is being created:
Proc.singleton_class.prepend(Module.new do
def new(*args, &cb)
puts "PROC #{[block_given?, cb, *args].inspect}"
super
end
end)
Proc.prepend(Module.new do
def initialize(*args, &cb)
puts "INIT #{[block_given?, cb, *args].inspect}"
super
end
def call(*args, &cb)
puts "CALL #{[block_given?, cb, *args].inspect}"
super
end
end)
λ = ->(*args) { }
[1].each &λ
#⇒ [1]
As one might see, neither the call to Proc::new
happened, nor Proc#initialize
and/or Proc#call
were called.
The question is: how ruby creates and executes a block wrapper under the hood?
NB Don’t test the code above in pry
/irb
console: they known to have glitches with pure execution of this, basically because they patch procs.
There has been some discussion of this behavior on the Ruby Issue Tracker, see Feature #10499: Eliminate implicit magic in Proc.new
and Kernel#proc
.
This is an implementation artifact of YARV: YARV pushes a block on the global VM stack, and Proc::new
simply creates a Proc
from the topmost block on the stack. So, if you happen to call Proc.new
from within a method which was called with a block, it will happily grab whatever block is on top of the stack, without ever checking where it came from. Somehow, somewhere, in the mist of time, this (let's call it) "accidental artifact" (I'd actually rather call it a bug) became a documented feature. A feature that the developers of JRuby (and presumably Rubinius, Opal, MagLev, etc.) would rather get rid of.
Since most other implementations work completely differently, this behavior which comes "for free" on YARV, makes both blocks and Proc::new
pontetially more expensive on other implementations and prohibits possible optimizations (which doesn't hurt on YARV, because YARV doesn't optimize).
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