如何在传递给`instance_exec`时执行proc

时间:2016-11-22 10:28:12

标签: ruby lambda metaprogramming proc

问题的灵感来自this one

Proc::new可以选择在方法中没有块的情况下调用:

  只有在带有附加块的方法中,才能在没有块的情况下调用

Proc::new,在这种情况下,该块将转换为Proc对象。

proc / lambda实例作为代码块传递时,正在创建Proc的新实例:

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]

正如人们可能看到的那样,Proc::new的呼叫既未发生,也未呼叫Proc#initialize和/或Proc#call

问题是: ruby​​如何在引擎盖下创建和执行块包装器?

NB 不要在pry / irb控制台中测试上面的代码:他们知道有纯粹执行此问题的故障,主要是因为他们修补了proc。 / p>

1 个答案:

答案 0 :(得分:2)

在Ruby Issue Tracker上已经讨论过这种行为,请参阅Feature #10499: Eliminate implicit magic in Proc.new and Kernel#proc

这是YARV的实现工件:YARV在全局VM堆栈上推送块,而Proc::new只是从堆栈的最顶层块创建Proc。因此,如果您碰巧在使用块调用的方法中调用Proc.new,它将很乐意抓住堆栈顶部的任何块,而无需检查它来自何处。不知何故,在某个地方,在时间的迷雾中,这个(让他们称之为)"偶然的神器" (我实际上把它称为一个bug)成为一个记录的功能。 JRuby(可能是Rubinius,Opal,MagLev等)的开发人员宁愿摆脱这一功能。

由于大多数其他实现的工作方式完全不同,因此这种行为免费提供了#34;在YARV上,使得两个块和Proc::new在其他实现上的成本更高,并禁止可能的优化(这对YARV没有影响,因为YARV没有优化)。

相关问题