Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is a proc object preserved under `&`-`&` roundtrip?

Tags:

ruby

proc

When I splat an array with * when passing it to a method, then reconstruct an array from it with * within the method, the identity of the array is not preserved:

a = []
a.object_id # => 69846339548760
def bar *a; a.object_id end
bar(*a) # => 69846339537540

However, when I turn a proc into a block with & when passing it to a method, then reconstruct a proc from the block with & within the method, the identity of the proc seems to be preserved:

pr = ->{}
pr.object_id # => 69846339666160
def foo ≺ pr.object_id end
foo(&pr) # => 69846339666160

How is the proc object preserved? Isn't it lost when it is converted to a block? Is this a guaranteed behaviour?

like image 260
sawa Avatar asked Oct 30 '22 10:10

sawa


1 Answers

Ruby VM is a stackmachine. When calling a function, it puts all its arguments (including self) onto the stack and then calls.

How array splat works - it takes array contents and puts it onto the stack, then calls function:

> puts RubyVM::InstructionSequence.compile("a = []; func *a").disasm
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 2] a          
0000 trace            1                                               (   1)
0002 newarray         0
0004 setlocal_OP__WC__0 2
0006 putself          
0007 getlocal_OP__WC__0 2
0009 splatarray       false
0011 opt_send_without_block <callinfo!mid:func, argc:1, ARGS_SPLAT|FCALL>
0013 leave  

When passing proc as a block, similar thing happens, but ruby does not have to unwrap proc, it is already a proc.

Added: as per RubySpec's https://github.com/ruby/spec/blob/master/core/proc/block_pass_spec.rb

def revivify; Proc.new; end

it "remains the same object if re-vivified by the target method" do
  p = Proc.new {}
  p2 = revivify(&p)
  p.object_id.should == p2.object_id
  p.should == p2
end

this is somewhat standardized behavior, so at least should be followed by Rubinius and jRuby

like image 78
Vasfed Avatar answered Nov 14 '22 05:11

Vasfed