Como proc é executado quando passado para 'instance_exec'

9

A pergunta é inspirada por este .

Proc::new tem uma opção para ser chamada sem um bloco dentro de um método:

  

Proc::new pode ser chamado sem um bloco somente dentro de um método com um bloco anexado, caso em que esse bloco é convertido para o objeto Proc .

Quando a instância proc / lambda é passada como um bloco de códigos, a nova instância de Proc está sendo criada:

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]

Como se pode ver, nem a chamada para Proc::new aconteceu, nem Proc#initialize e / ou Proc#call foram chamados.

A questão é: como o ruby cria e executa um wrapper de bloco sob o capô?

NB Não teste o código acima em pry / irb console: eles sabem que têm falhas com a execução pura disso, basicamente porque corrigem procs.

    
por Aleksei Matiushkin 22.11.2016 в 11:28
fonte

1 resposta

2

Houve alguma discussão sobre esse comportamento no Ruby Issue Tracker, consulte Recurso # 10499: Elimine a magia implícita em Proc.new e Kernel#proc .

Este é um artefato de implementação do YARV: YARV envia um bloco na pilha global da VM, e Proc::new simplesmente cria um Proc do bloco mais alto na pilha. Então, se acontecer de você chamar Proc.new de dentro de um método que foi chamado com um bloco, ele ficará feliz em pegar qualquer bloco que esteja no topo da pilha, sem nunca verificar de onde veio. De alguma forma, em algum lugar, na névoa do tempo, isso (vamos chamá-lo) de "artefato acidental" (na verdade eu chamaria isso de bug) tornou-se um recurso documentado. Uma característica que os desenvolvedores do JRuby (e presumivelmente Rubinius, Opal, MagLev, etc.) prefeririam se livrar.

Como a maioria das outras implementações funciona de forma completamente diferente, esse comportamento que vem "de graça" no YARV torna os blocos e Proc::new mais caros em outras implementações e proíbe possíveis otimizações (o que não atrapalha no YARV, porque YARV não otimizar).

    
por Jörg W Mittag 26.11.2016 / 19:20
fonte