Bug #10006
closedinstance_exec uses incorrect binding with curried proc
Description
After using Proc#curry
, instanve_eval
/instance_exec
does not seem to work:
class Test
def test_func
140
end
end # => :test_func
p = ->(x, y) { x + y + test_func } # => #<Proc:0x007fd23b12bc80@(irb):6 (lambda)>
test = Test.new # => #<Test:0x007fd23b0f3d08>
test.instance_exec(1, 2, &p) # => 143
curried = p.curry[1] # => #<Proc:0x007fb3142be070 (lambda)>
test.instance_exec(2, &curried)
NameError: undefined local variable or method `test_func' for main:Object
from (irb):6:in `block in irb_binding'
from (irb):10:in `instance_exec'
from (irb):10
from ./bin/irb:11:in `<main>'
Updated by nobu (Nobuyoshi Nakada) over 9 years ago
- Description updated (diff)
Updated by nobu (Nobuyoshi Nakada) over 9 years ago
It may need further consideration whether instance_exec
should affect the wrapped proc.
Updated by jeremyevans0 (Jeremy Evans) over 4 years ago
I think the current behavior is the behavior that should be expected. It mirrors the behavior you would expect if you implemented Proc#curry
in Ruby:
class Test
def test_func
140
end
end # => :test_func
p = ->(x, y) { x + y + test_func }
curried = ->(y) { p.call(1, y) }
test.instance_exec(2, &curried)
Changing Proc#curry
to internally use instance_exec
for curried procs would fix this case and break the opposite case:
class Test
def test_func
140
end
def p
->(x, y) { x + y + test_func }
end
end
# Current behavior: works
curried = Test.new.p.curry[1]
# Proposed behavior using instance_exec: breaks
#curried = ->(y) { instance_exec(1, y, &Test.new.p) }
instance_exec(2, &curried)
That being said, there is a valid argument that instance_exec(arg, &proc)
and instance_exec(&proc.curry[arg])
should operate the same way, just as proc.call(arg)
and proc.curry[arg].call
operate the same way.
Updated by ko1 (Koichi Sasada) over 4 years ago
- Assignee set to mame (Yusuke Endoh)
Updated by mame (Yusuke Endoh) over 4 years ago
- Status changed from Open to Rejected
I'm for the first interpretation of @jeremyevans0 (Jeremy Evans) . The semantics is simple and clear.
If instance_eval
affects the context of curried proc, we need to decide how to handle an intermediate call to instance_eval
that does not invoke the original proc:
f = -> (x, y) { p [x, y, self] }
f1 = f.curry
f2 = "1".instance_exec(1, &f1)
r = "2".instance_exec(2, &f2) #=> [1, 2, ???]
We may respect only the last call to instance_eval
by ignoring other calls, but it looks more complex than needed. I prefer the simplicity.