Skip to content
This repository

Implicit blocks from Symbol#to_proc does surprising and wrong things (with --1.9) #274

Closed
bjeanes opened this Issue August 29, 2012 · 8 comments

3 participants

Bo Jeanes Charles Oliver Nutter Bradley Schaefer
Bo Jeanes

I've had trouble articulating this, so hopefully the following code will make it pretty explanatory:

Code

(Variants also available https://gist.github.com/3519907 and https://gist.github.com/3519564)

class Hash
  def map_values
    map { |k, v| [k, yield(v)] }
  end
end

p data = {"x"=>[{"y"=>42}]}

p(data.map_values { |x| x.first })
p data

p data.map_values(&:first)
p data

Correct output (jruby-1.6.7.2 --1.8, jruby-1.7.0-preview2 --1.8, mri 1.8.7, mri 1.9.2-p290)

{"x"=>[{"y"=>42}]}
[["x", {"y"=>42}]]
{"x"=>[{"y"=>42}]}
[["x", {"y"=>42}]]
{"x"=>[{"y"=>42}]}

Incorrect output (jruby-1.6.7.2 --1.9, jruby-1.7.0-preview2 --1.9)

{"x"=>[{"y"=>42}]}
[["x", {"y"=>42}]]
{"x"=>[{"y"=>42}]}
[["x", ["y", 42]]]
{"x"=>[]}

In my specific case, not only does my method (contrived — simplified from a real world project) very much return the wrong thing, it mutates the original Hash, even though no mutative methods were used.

Bo Jeanes

I believe @lopex and @qmx have both replicated this bug in IRC

Bo Jeanes

Note that using this as the method definition does work correctly:

class Hash
  def map_values(&block)
    map { |k, v| [k, block.call(v)] }
  end
end
Charles Oliver Nutter
Owner

This is likely the same as some other block/proc dispatch issues where we're unwrapping too much or not unwrapping enough. Sigh.

Bo Jeanes

Even adding a dup doesn't stop the mutation of the original hash. This is horrendously surprising behavior :(

class Hash
  def map_values
    dup.map { |k, v| [k, yield(v)] }
  end
end
Bradley Schaefer

I think I have struck upon this problem as well... except I am getting an NPE.

    parser.for_tag(:blurb).inject([]) { |result, value| result << value } # this works
    parser.for_tag(:blurb).inject([], :<<) # NPE (see gist)

Example of NPE generated by inject with :<<
The spec generating the NPE

Not sure if it has something to do with the fact that I mix in Enumerable with my own each implementation (which uses block.call(value) fwiw), because I wasn't able to reproduce this with a simple Array#inject

I see this behavior only in jruby-1.6.7.2 --1.9 and jruby-1.7.0-preview2 --1.9

Bradley Schaefer

Tried to boil down my inject issue to a much simpler test case

class Foo
  include Enumerable

  def initialize(*stuff)
    @stuff = stuff
  end

  def each(&block)
    @stuff.each { |thing| block.call(thing) }
  end
end

puts Foo.new(1, 2, 3).inject([]) { |result, value| result << value } # works
puts Foo.new(1, 2, 3).inject([], :<<) # NPE
Charles Oliver Nutter
Owner

The original issue appears to be fixed now, probably due to the change I made to fix Symbol#to_proc argument handling earlier today (a964aa4).

The additional issue with index and :<< is probably something different, but I'll look into that too.

Charles Oliver Nutter
Owner

Ok, so the new case with << actually works if you procify the symbol first, a la inject([], :<<). It only fails if you pass the symbol directly in, which should do the to_proc coercing for you exactly the same way. So inject is probably coercing wrong.

Charles Oliver Nutter headius closed this issue from a commit September 08, 2012
Charles Oliver Nutter Fix #274 (second part)
Our Java-based #inject block did not report it wanted any args,
and it seems we were not giving it any. Modified to be "OPTIONAL"
and all cases seem to work.
bbe2e8f
Charles Oliver Nutter headius closed this in bbe2e8f September 08, 2012
Christian Meier mkristian referenced this issue from a commit September 08, 2012
Charles Oliver Nutter Fix #274 (second part)
Our Java-based #inject block did not report it wanted any args,
and it seems we were not giving it any. Modified to be "OPTIONAL"
and all cases seem to work.
713f911
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.