2014-07-02 4 views
8

Załóżmy, że posiada klasy, takie jak to:Możliwe do instance_eval curried proc?

class Test 
    def test_func 
    140 
    end 
end 

I Proc, który odwołuje się do funkcji użytkownika z Test:

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

aby wywołać p, powiązać go z wystąpieniem Test:

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

załóżmy teraz chcę przekazać tylko y do p i zawsze przechodzą x = 1:

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

Idealnie powinienem być w stanie po prostu instance_exec jak poprzednio, ale zamiast:

test.instance_exec(2, &curried) 

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

PROC biegnie w to, co wydaje się być niepoprawny wiążące. Co daje?

+1

Cóż, wygląda na to, że podczas zamykania funkcji wiąże zmienne 'test_func', niezależnie od lokalnego' test_func'. Próbuję wymyślić, dlaczego tak powinno być i nie mogę wymyślić niczego, także grając w kółko, nie mogę znaleźć żadnego sposobu uzyskania oczekiwanego wyniku (poprawnie powiązanego 'test_func' na zwiniętym procesie). Fajne pytanie. –

+2

Tak, to jest interesujące. Wygląda to tak, jak po przekierowaniu proca wiąże on zakres do głównego i pomimo jego oceny w ramach testu utrzymuje zakres do głównego. – jvans

+0

Zgłosiłem to jako błąd [tutaj] (https: //bugs.ruby-lang.org/issues/10006), Zobaczymy, czy rzeczywiście tak jest lub czy ktoś może to wyjaśnić. –

Odpowiedz

3

Tak, wierzę, że to błąd.

Myślę, że sprowadza się to do faktu, że curry zwraca "Procent poziomu C" zamiast zwykłego proc. Nie w pełni rozumiem różnicę między tymi dwoma (domyślam się, że ten pierwszy jest stworzony przez kod Ruby C, który jest tym, co robi curry), ale można powiedzieć, że są one różne, gdy próbujesz podjąć wiązanie.

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

Patrząc na the source, to wygląda na to ich wewnętrzne reprezentacje struct mieć różne wartości dla państw iseq, który mówi, jaki rodzaj sekwencji rozkazów ten blok posiada.

Jest to istotne, gdy dzwonisz instance_exec, który ostatecznie kończy się wywołaniem invoke_block_from_c w vm.c, który rozgałęzia się w zależności od rodzaju iseq:

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

Oddział ja przegapiłem (...) kończy się wywołaniem vm_push_frame co wygląda jak w środowisku, w którym nie ma takiej wartości, jak vm_yield_with_cfunc.

Zgaduję, że dlatego, że curried proc jest tworzony w kodzie C i kończy się na innym "typie" niż twój pierwszy proc, druga gałąź jest brana w powyższym fragmencie, a środowisko nie jest używane.

muszę podkreślić, że wszystko to jest bardzo spekulacyjny na podstawie odczytu kodu, nie uruchamiać żadnych testów lub próbował coś z (i jestem również nie wszystko że zna wewnętrznego Ruby tak!)

+0

Dobre znalezisko - również sprawdziłem kod C, ale tego nie zauważyłem! –