Project

General

Profile

Actions

Bug #10006

closed

instance_exec uses incorrect binding with curried proc

Added by ledbettj (John Ledbetter) over 9 years ago. Updated over 4 years ago.

Status:
Rejected
Target version:
-
ruby -v:
ruby 2.2.0dev (2014-07-02 trunk 46660) [x86_64-darwin13]
[ruby-core:63522]

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.

Actions #3

Updated by naruse (Yui NARUSE) about 6 years ago

  • Target version deleted (2.2.0)

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.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0