2014-07-02 31 views
8

假設我有一類像這樣:可能對一個咖喱過程的instance_eval?

class Test 
    def test_func 
    140 
    end 
end 

而且一個進程,它引用從Test一個成員函數:

p = ->(x, y) { x + y + test_func } # => #<Proc:[email protected](pry):6 (lambda)> 

要調用p,我把它綁定到Test一個實例:

test = Test.new      # => #<Test:0x007fb3143c5a68> 
test.instance_exec(1, 2, &p)  # => 143 

現在假設我想通過yp,始終通過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

好吧,它似乎在currying函數中將變量'test_func'綁定爲本地'test_func'。我試圖想到爲什麼這應該是,不能拿出任何東西,也玩弄我找不到任何獲得預期結果的方式(在curried proc上正確綁定'test_func')。很好的問題。 –

+2

是的,這是一個有趣的。它看起來像在處理完proc之後將範圍綁定到main,並且儘管在測試中對其進行了評估,但它仍然將範圍設置爲main。 – jvans

+0

我報告這是一個錯誤[這裏](https://bugs.ruby-lang。org/issues/10006),我們會看看它是否真的存在,或者是否有人可以解釋。 –

回答

3

是的,我相信這是一個錯誤。

我認爲這歸結於curry返回一個「C級過程」而不是正常的過程。我不完全理解兩者之間的區別(我猜測前者是由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_cvm.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不同的「類型」結束,所以其他分支將在上面的代碼片段中使用,並且不會使用環境。

我應該指出的是,這一切是相當投機基於閱讀的代碼,我沒有運行任何測試或嘗試新鮮事物了(我也不所有是熟悉內部紅寶石反正!)

+0

好的發現 - 我也在C代碼中探索,但沒有抓住! –