Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

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

Closed
bjeanes opened this Issue · 8 comments

3 participants

@bjeanes

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.

@bjeanes

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

@bjeanes

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
@headius
Owner

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

@bjeanes

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
@soulcutter

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

@soulcutter

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
@headius
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.

@headius
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.

@headius headius closed this issue from a commit
@headius headius 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
@headius headius closed this in bbe2e8f
@mkristian mkristian referenced this issue from a commit
@headius headius 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.