2014-07-02 19 views
8

Supponiamo che ho una classe come questa:Possibile per esempio_eval un proc al curry?

class Test 
    def test_func 
    140 
    end 
end 

E un proc, che fa riferimento a una funzione membro da Test:

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

Per chiamare p, mi legano a un'istanza di Test:

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

Ora supponiamo che io voglio passare solo y-p, e sempre passare x = 1:

curried = p.curry[1]    # => #<Proc:0x007fb3142be070 (lambda)> 

Idealmente mi dovrebbe essere in grado di appena instance_exec come prima, ma invece:

test.instance_exec(2, &curried) 

=> NameError: undefined local variable or method `test_func' for main:Object 

Il proc viene eseguito in quello che sembra essere il legame corretto. Cosa dà?

+1

beh, sembra che nel far funzionare la funzione esso leghi le variabili 'test_func' in qualsiasi cosa sia il' test_func' locale. Sto cercando di pensare perché questo dovrebbe essere e non riesco a inventare nulla, anche giocando non riesco a trovare alcun modo di ottenere il risultato atteso (un 'test_func' legato correttamente su un proc elaborato). Bella domanda –

+2

Sì, questo è interessante. Sembra che dopo aver eseguito il proc si leghi lo scope al main e nonostante lo abbia valutato all'interno del test, mantiene l'ambito al main. – jvans

+0

Ho segnalato questo come un bug [qui] (https: //bugs.ruby-lang.org/issues/10006), vedremo se è effettivamente o se qualcuno può spiegare. –

risposta

3

Sì, credo che questo sia un bug.

Penso che dipenda dal fatto che curry restituisce un "proc di livello C" anziché un proc normale. Non capisco appieno la differenza tra i due (suppongo che il primo sia creato dal codice Ruby C che è quello che fa curry), ma puoi dire che sono diversi quando provi a prendere un legame.

p.binding # => #<Binding:0x000000020b4238> 
curried.binding # => ArgumentError: Can't create a binding from C level Proc 

Guardando the source, questo sembra loro rappresentazioni interne struct hanno valori diversi per la iseq membro, che dice che tipo di sequenza di istruzioni che questo blocco detiene.

Questo è importante quando si chiama instance_exec, che alla fine finisce per chiamare invoke_block_from_c in vm.c, che si dirama a seconda del tipo iseq:

else if (BUILTIN_TYPE(block->iseq) != T_NODE) { 
    ... 
} else { 
    return vm_yield_with_cfunc(th, block, self, argc, argv, blockptr); 
} 

Il ramo Ho perso (...) finisce per chiamare vm_push_frame con quello che sembra un ambiente dove non lo è il vm_yield_with_cfunc.

Quindi la mia ipotesi sarebbe che, poiché il proc elaborato viene creato nel codice C e finisce con un "tipo" diverso da quello del primo proc, l'altro ramo viene preso nello snippet sopra riportato e l'ambiente non viene utilizzato.

Vorrei sottolineare che tutto questo è piuttosto speculativa basata sulla lettura del codice, non ho eseguito alcun test o provato nulla (e sono, inoltre, non tutto che la familiarità con Ruby interna comunque!)

+0

Buona scoperta - ho anche cercato nel codice C ma non l'ho capito! –

Problemi correlati