假设我有一个这样的类:
class Test def test_func 140 end end
还有一个proc,它引用了Test中的成员函数:
p = ->(x,y) { x + y + test_func } # => #<Proc:0x007fb3143e7f78@(pry):6 (lambda)>
要调用p,我将它绑定到Test的一个实例:
test = Test.new # => #<Test:0x007fb3143c5a68> test.instance_exec(1,2,&p) # => 143
现在假设我想将y传递给p,并且总是传递x = 1:
curried = p.curry[1] # => #<Proc:0x007fb3142be070 (lambda)>
理想情况下,我应该像以前一样只需要instance_exec,而是:
test.instance_exec(2,&curried) => NameError: undefined local variable or method `test_func' for main:Object
proc在似乎不正确的绑定中运行.是什么赋予了?
解决方法
是的,我相信这是一个错误.
我认为这归结为curry返回“C级proc”而不是正常proc的事实.我不完全理解两者之间的区别(我猜测前者是由Ruby C代码创建的,这是curry所做的),但是当你尝试接受绑定时,你可以告诉它们是不同的.
p.binding # => #<Binding:0x000000020b4238> curried.binding # => ArgumentError: Can't create a binding from C level Proc
通过查看the source,看起来它们的内部结构表示对于iseq成员具有不同的值,该成员表示该块所具有的指令序列类型.
当你调用instance_exec时这很重要,它最终在vm.c中调用invoke_block_from_c,它根据iseq类型进行分支:
else if (BUILTIN_TYPE(block->iseq) != T_NODE) { ... } else { return vm_yield_with_cfunc(th,block,self,argc,argv,blockptr); }
我错过的分支(…)最终调用vm_push_frame,看起来像某些环境,而vm_yield_with_cfunc没有.
所以我的猜测是因为curried的proc是用C代码创建的,并且结果与你的第一个proc不同,所以另一个分支是在上面的代码片段中使用的,并且不使用环境.
我应该指出,所有这些都是基于阅读代码的非常推测,我没有运行任何测试或尝试过任何东西(而且我也不是那么熟悉内部Ruby!)