Project

General

Profile

Actions

Bug #4323

closed

Proc#hash is Ill-Behaved

Added by runpaint (Run Paint Run Run) about 13 years ago. Updated over 11 years ago.

Status:
Closed
Target version:
-
ruby -v:
ruby 1.9.3dev (2011-01-19 trunk 30597) [x86_64-linux]
Backport:
[ruby-core:34854]

Description

=begin
Proc#hash is not predictable for receivers that are #eql?

 irb> ->{}.hash
 => -1250381286238705236
 irb> ->{}.hash
 => 2684657672161532106
 irb> ->{true}.hash
 => -2939885723276364833
 irb> ->{true}.hash
 => -3124033653404588619
 irb> proc{}.hash
 => -47014914982973166
 irb> proc{}.hash
 => -744699484074399895

=end


Files

proc-hash.patch (650 Bytes) proc-hash.patch runpaint (Run Paint Run Run), 01/26/2011 06:35 PM

Related issues 1 (0 open1 closed)

Related to Ruby master - Bug #4559: Proc#== does not match the documented behaviourClosedko1 (Koichi Sasada)04/07/2011Actions
Actions #1

Updated by headius (Charles Nutter) about 13 years ago

=begin
I'm not so sure I'd expect Proc#hash to be equal in these cases. Of course, I don't feel like Procs that simply have the same code and closure should be expected to be eql? either.

Each proc has its own scope and its own reference to closed-over containing scope. They can be used independently and have independent state of their own. Should they be eql? and have identical hash values?

What constitutes equivalent procs? Just the code it contains? That's obviously not enough, since they'll have access to different closed-over state. The code it contains and the lexical scope in which it is instantiated? That's not consistently supported either, for whatever reason:

~/projects/jruby ➔ ruby1.9 -e "x = ->{def foo; end}; y = ->{def foo; end}; p x.eql? y"
false

~/projects/jruby ➔ ruby1.9 -e "x = ->{:foo}; y = ->{:foo}; p x.eql? y"
true

~/projects/jruby ➔ ruby1.9 -e "x = ->{'foo'}; y = ->{'foo'}; p x.eql? y"
false

~/projects/jruby ➔ ruby1.9 -e "x = ->{if true; false; end}; y = ->{if true; false; end}; p x.eql? y"
true

~/projects/jruby ➔ ruby1.9 -e "x = ->{->{}}; y = ->{->{}}; p x.eql? y"
false

~/projects/jruby ➔ ruby1.9 -e "x = ->{5}; y = ->{5}; p x.eql? y"
true

~/projects/jruby ➔ ruby1.9 -e "x = ->{5.0}; y = ->{5.0}; p x.eql? y"
false

~/projects/jruby ➔ ruby1.9 -e "x = ->{[]}; y = ->{[]}; p x.eql? y"
true

~/projects/jruby ➔ ruby1.9 -e "x = ->{[5.0]}; y = ->{[5.0]}; p x.eql? y"
false

~/projects/jruby ➔ ruby1.9 -e "x = proc {}; y = lambda {}; p x.eql? y"
true

I only searched for a few minutes and came up with all these inconsistencies in 1.9's Proc#eql?. Obviously the containing scope and the code within the proc are not enough to determine eql?, and simple cases like ->{'foo'}, which should be easy to call "equivalent", make me believe it's not a good idea to trust Proc#eql? in any case anyway. And if you can't trust eql? I'm not sure how useful it is to trust hash.

Maybe someone more familiar with MRI can explain what criteria should be used to determine equivalence of two procs? I can't figure it out from the above examples.

There may also be evidence here that this is a very implementation-specific detail. In JRuby, when a given Proc has been JIT-compiled, should it still be eql? the same proc that has not been compiled? In the case of precompiling a Proc, so that the original AST no longer exists at runtime, what would be use to determine the proc is equivalent to some other proc?
=end

Actions #2

Updated by headius (Charles Nutter) about 13 years ago

=begin
I believe your example case, run in IRB, is also flawed. Witness:

~/projects/jruby ➔ irb1.9

->{}
=> #<Proc:0x00000100aba6c0@(irb):1 (lambda)>
->{}
=> #<Proc:0x00000100ab8cf8@(irb):2 (lambda)>
->{}
=> #<Proc:0x00000100ab64a8@(irb):3 (lambda)>

Oh ho you say, but that doesn't mean they should not be equivalent. Au contraire:

a = []
=> []
a << ->{}
=> [#<Proc:0x00000100afee60@(irb):2 (lambda)>]
a << ->{}
=> [#<Proc:0x00000100afee60@(irb):2 (lambda)>, #<Proc:0x00000100afb170@(irb):3 (lambda)>]
a[0].eql?(a[1])
=> false

IRB is a bad place to demonstrate such things because the bindings are changing around all the time. Your examples do show different hash values (and your expected eql? results) within a script, however.
=end

Actions #3

Updated by runpaint (Run Paint Run Run) about 13 years ago

=begin

I'm not so sure I'd expect Proc#hash to be equal in these cases. Of course, I don't feel like
Procs that simply have the same code and closure should be expected to be eql? either.

Perhaps, but the Rdoc is clear that the current behaviour of #eql? is intended, and that of #hash is wrong. If you want the spec changed, thus breaking backward compatibility, that's another discussion.

I've attached my best effort at a fix. It doesn't work for Method#to_proc cases because #eql? doesn't, either. With the patch applied:

 run@desktop:~/mir/ruby$  →  ./ruby --disable-gems -e 'r=/#{1+2}/; p ->{:FOO; r}.hash; p ->{:FOO; r}.hash'
 661952060
 661952060
 run@desktop:~/mir/ruby$  →  ./ruby --disable-gems -e 'r=/#{1+2}/; p ->{:FOO; r}.hash; p ->{:FOO; r; 22}.hash'
 -522099608
 77097371

=end

Updated by naruse (Yui NARUSE) over 12 years ago

  • Status changed from Open to Assigned
  • Assignee set to matz (Yukihiro Matsumoto)

Updated by ko1 (Koichi Sasada) over 11 years ago

  • Status changed from Assigned to Closed
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0