Suppose I have a class such as this:
class Test
def test_func
140
end
end
And a proc, which references a member function from Test
:
p = ->(x, y) { x + y + test_func } # => #<Proc:0x007fb3143e7f78@(pry):6 (lambda)>
To call p
, I bind it to an instance of Test
:
test = Test.new # => #<Test:0x007fb3143c5a68>
test.instance_exec(1, 2, &p) # => 143
Now suppose I want to pass just y
to p
, and always pass x = 1
:
curried = p.curry[1] # => #<Proc:0x007fb3142be070 (lambda)>
Ideally I should be able to just instance_exec
as before, but instead:
test.instance_exec(2, &curried)
=> NameError: undefined local variable or method `test_func' for main:Object
The proc runs in what seems to be the incorrect binding. What gives?
Yes, I believe this is a bug.
I think it comes down to the fact that curry
returns a "C level proc" rather than a normal proc. I don't fully understand the difference between the two (I'm guessing the former is one created by the Ruby C code which is what curry
does), but you can tell they're different when you try and take a binding.
p.binding # => #<Binding:0x000000020b4238>
curried.binding # => ArgumentError: Can't create a binding from C level Proc
By looking at the source, this looks like their internal struct representations have different values for the iseq
member, which says what kind of instruction sequence this block holds.
This is significant when you call instance_exec
, which eventually ends up calling invoke_block_from_c
in vm.c, which branches depending on the iseq
type:
else if (BUILTIN_TYPE(block->iseq) != T_NODE) {
...
} else {
return vm_yield_with_cfunc(th, block, self, argc, argv, blockptr);
}
The branch I missed out (...
) ends up calling vm_push_frame
with what looks like some environment where as vm_yield_with_cfunc
doesn't.
So my guess would be that because the curried proc is created in C code and ends up of a different 'type' than your first proc, the other branch is taken in the above snippet and the enviornment isn't used.
I should point out that all of this is pretty speculative based on reading the code, I haven't run any tests or tried anything out (and I'm also not all that familiar with internal Ruby anyway!)
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