instance_eval可能是一个咖喱过程吗?

时间:2014-07-02 14:26:17

标签: ruby currying

假设我有一个这样的类:

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在似乎不正确的绑定中运行。是什么给了什么?

1 个答案:

答案 0 :(得分:4)

是的,我相信这是一个错误。

我认为归结为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时,这很有意义,invoke_block_from_c最终会调用vm.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看起来像{{1}}那样的环境。

所以我的猜测是因为curry proc是用C代码创建的,结果是与你的第一个proc不同的'type',另一个分支被带到上面的代码片段中并且没有使用环境。

我应该指出,所有这些都是基于阅读代码的相当推测,我没有运行任何测试或尝试任何东西(我也不是所有熟悉内部无论如何,Ruby!)