https://bugs.ruby-lang.org/https://bugs.ruby-lang.org/favicon.ico?17113305112009-06-28T22:22:43ZRuby Issue Tracking SystemRuby master - Feature #1697: Object#<=>https://bugs.ruby-lang.org/issues/1697?journal_id=44272009-06-28T22:22:43ZRickDeNatale (Rick DeNatale)rick.denatale@gmail.com
<ul></ul><p>=begin<br>
Well, a lot of users of <=> don't expect a nil result!</p>
<p>class Object<br>
def <=> other<br>
nil<br>
end<br>
end</p>
<p>1 <=> nil # => nil<br>
nil <=> 1 # => nil<br>
1 <=> false # => nil<br>
false <=> nil # => nil<br>
nil <=> false # => nil<br>
false <=> nil # => nil<br>
[1, nil, false].sort # =></p>
<a name="gt-15in-sort-comparison-of-Fixnum-with-nil-failed-ArgumentError"></a>
<h1 >~> -:15:in `sort': comparison of Fixnum with nil failed (ArgumentError)<a href="#gt-15in-sort-comparison-of-Fixnum-with-nil-failed-ArgumentError" class="wiki-anchor">¶</a></h1>
<a name="gt-from-15"></a>
<h1 >~> from -:15<a href="#gt-from-15" class="wiki-anchor">¶</a></h1>
<p>=end</p> Ruby master - Feature #1697: Object#<=>https://bugs.ruby-lang.org/issues/1697?journal_id=44282009-06-29T03:12:38Zrue (Eero Saynatkari)
<ul></ul><p>=begin<br>
Excerpts from Luiz Angelo Daros de Luca's message of Sun Jun 28 16:22:45 +0300 2009:</p>
<blockquote>
<p>Issue <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Object#<=> (Closed)" href="https://bugs.ruby-lang.org/issues/1697">#1697</a> has been updated by Rick DeNatale.</p>
<p>Well, a lot of users of <=> don't expect a nil result!</p>
</blockquote>
<p>For what it is worth, when Marc-Andre brought the topic up<br>
on #rubinius, I argued for it to always either return one<br>
of -1, 0, 1 or then raise an error; and to never return nil<br>
or any other value. The user may simply rescue (or not) and<br>
differentiate between errors as needed.</p>
<p>Additionally, there is no need to check for nil separately<br>
before working on the value.</p>
<p>One of the counterarguments was that errors are used for<br>
"control flow" here, but I do not see it that way. I would,<br>
in my code, probably never expect for #<=> to fail when I<br>
use it (and never guard it with rescue): to me it indicates<br>
a very likely design- or logic problem that should be fixed<br>
anyway. Exceptions, pun intended, of course may exist.</p>
<a name="Eero"></a>
<h2 >Eero<a href="#Eero" class="wiki-anchor">¶</a></h2>
<p>Magic is insufficiently advanced technology.</p>
<p>=end</p> Ruby master - Feature #1697: Object#<=>https://bugs.ruby-lang.org/issues/1697?journal_id=44372009-06-30T00:57:00Zrue (Eero Saynatkari)
<ul></ul><p>=begin<br>
Excerpts from Marc-Andre's message of Mon Jun 29 15:35:52 +0300 2009:</p>
<blockquote>
<p>This really is a different and separate discussion though; one could<br>
also argue that "nil =~ Date.new" should raise an error! Changing the<br>
semantics for <=> at this point would require a really compelling<br>
argument, since all existing Ruby code calling <=> or defining <=> has<br>
been written with the assumption that <=> should return nil when<br>
arguments can't be compared.</p>
</blockquote>
<p>I disagree. Because there are currently no unified semantics<br>
for #<=>, you are equally likely to find someone who relies<br>
on a TypeError (or NoMethodError) being raised on an invalid<br>
conversion. I think either change will require some people<br>
to change their code, making the argument moot in terms of<br>
which solution is "better."</p>
<p>(And indirectly, I think e.g. Array checks for a nil return<br>
value and raises if it gets one.)</p>
<p>Is there a compelling case where not being able to compare<br>
two objects is a valid expectation, and could not be better<br>
addressed by actually implementing the comparison or some<br>
other change in logic? On the flipside of that, there seems<br>
to be potential for masking problems by evaluating to nil,<br>
mostly in cases where #<=> is being used implicitly.</p>
<blockquote>
<p>My proposition doesn't change the semantics of <=>, rather it makes it<br>
is more respectful of it.</p>
</blockquote>
<p>See above.</p>
<a name="Eero"></a>
<h2 >Eero<a href="#Eero" class="wiki-anchor">¶</a></h2>
<p>Magic is insufficiently advanced technology.</p>
<p>=end</p> Ruby master - Feature #1697: Object#<=>https://bugs.ruby-lang.org/issues/1697?journal_id=56132009-09-05T06:50:02Znobu (Nobuyoshi Nakada)nobu@ruby-lang.org
<ul></ul><p>=begin<br>
Hi,</p>
<p>At Sun, 28 Jun 2009 10:48:45 +0900,<br>
Marc-Andre Lafortune wrote in <a href="/issues/1697">[ruby-core:24063]</a>:</p>
<blockquote>
<p>Other comparison operators like <, <=, >=, > would also gain<br>
in consistency if they were defined in terms of <=>. This<br>
way, 0 < nil and nil > 0 would raise the same errors instead<br>
of errors of different types. This is secondary to the main<br>
question: is it better to define Object#<=> or not?<br>
My vote is on 'yes'.</p>
</blockquote>
<p>A patch for it.</p>
<h1>
<br>
Index: object.c</h1>
<p>--- object.c (revision 24752)<br>
+++ object.c (working copy)<br>
@@ -32,5 +32,5 @@ VALUE rb_cTrueClass;<br>
VALUE rb_cFalseClass;</p>
<p>-static ID id_eq, id_eql, id_match, id_inspect, id_init_copy;<br>
+static ID id_eq, id_eql, id_match, id_inspect, id_init_copy, id_cmp;</p>
<p>/*<br>
@@ -1116,4 +1116,14 @@ rb_obj_not_match(VALUE obj1, VALUE obj2)<br>
}</p>
<p>+/* :nodoc: */<br>
+static VALUE<br>
+rb_obj_cmp(VALUE obj, VALUE arg)<br>
+{</p>
<ul>
<li>if (!rb_respond_to(arg, id_cmp)) {</li>
<li>rb_notimplement();</li>
<li>}</li>
<li>return Qnil;<br>
+}</li>
<li>
</ul>
<p>/***********************************************************************<br>
@@ -2518,4 +2528,5 @@ Init_Object(void)<br>
rb_define_method(rb_mKernel, "=~", rb_obj_match, 1);<br>
rb_define_method(rb_mKernel, "!~", rb_obj_not_match, 1);</p>
<ul>
<li>
<p>rb_define_method(rb_mKernel, "<=>", rb_obj_cmp, 1);<br>
rb_define_method(rb_mKernel, "eql?", rb_obj_equal, 1);<br>
rb_define_method(rb_mKernel, "hash", rb_obj_hash, 0);<br>
@@ -2659,4 +2670,5 @@ Init_Object(void)<br>
id_inspect = rb_intern("inspect");<br>
id_init_copy = rb_intern("initialize_copy");</p>
</li>
<li>
<p>id_cmp = rb_intern("<=>");</p>
<p>for (i=0; conv_method_names[i].method; i++) {<br>
</p>
</li>
</ul>
<p>--<br>
Nobu Nakada</p>
<p>=end</p> Ruby master - Feature #1697: Object#<=>https://bugs.ruby-lang.org/issues/1697?journal_id=62092009-10-13T11:40:44Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<ul></ul><p>=begin<br>
I would modify slightly Nobu's patch to return 0 if the two compared objects are one and the same. I also think it is probably a better choice to return nil instead of raising an error.</p>
<p>With this patch in, running 'make test' doesn't generate any error. 'make test-all' generates two, on for rake because Rake::FileList has a weak way to find which methods are proper to Array, and another error because of Delegator, which doesn't inherit from BasicObject (see issue 1333) and doesn't list :<=> in its list of methods to undefine. So both of these are trivially fixed.</p>
<p>Although I really would like an implementation of a good double dispatch scheme, I'll leave that discussion for another time.</p>
<p>diff --git a/object.c b/object.c<br>
index e81007b..fdf1ee6 100644<br>
--- a/object.c<br>
+++ b/object.c<br>
@@ -1115,13 +1115,23 @@ rb_obj_not_match(VALUE obj1, VALUE obj2)<br>
return RTEST(result) ? Qfalse : Qtrue;<br>
}</p>
<p>-/* :nodoc: <em>/<br>
+/</em></p>
<ul>
<li>
<ul>
<li>call-seq:</li>
</ul>
</li>
<li>
<ul>
<li>
<pre><code>obj <=> other => -1, 0, 1 or nil
</code></pre>
</li>
</ul>
</li>
<li>
<ul>
<li>
</ul>
</li>
<li>
<ul>
<li>Comparison---Returns -1 if <i>obj</i> is smaller than <i>other</i>,</li>
</ul>
</li>
<li>
<ul>
<li>0 if <i>obj</i> is the same as <i>other</i>, and +1 if <i>obj</i> is</li>
</ul>
</li>
<li>
<ul>
<li>greater than <i>other</i>. Returns <code>nil</code> if <i>obj</i>
</li>
</ul>
</li>
<li>
<ul>
<li>can not be compared with <i>other</i>.</li>
</ul>
</li>
<li>
<ul>
<li>Typically, this method is overridden in descendent</li>
</ul>
</li>
<li>
<ul>
<li>classes to provide class-specific meaning.</li>
</ul>
</li>
<li>*/</li>
<li>
</ul>
<p>static VALUE<br>
rb_obj_cmp(VALUE obj, VALUE arg)<br>
{</p>
<ul>
<li>if (!rb_respond_to(arg, id_cmp)) {</li>
<li>
<pre><code> rb_notimplement();
</code></pre>
</li>
<li>}</li>
</ul>
<ul>
<li>if (obj == arg)</li>
<li>
<pre><code> return INT2FIX(0);
</code></pre>
return Qnil;<br>
}</li>
</ul>
<p>=end</p> Ruby master - Feature #1697: Object#<=>https://bugs.ruby-lang.org/issues/1697?journal_id=64212009-10-25T01:57:05Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Closed</i></li><li><strong>% Done</strong> changed from <i>0</i> to <i>100</i></li></ul><p>=begin<br>
This issue was solved with changeset r25453.<br>
Marc-Andre, thank you for reporting this issue.<br>
Your contribution to Ruby is greatly appreciated.<br>
May Ruby be with you.</p>
<p>=end</p> Ruby master - Feature #1697: Object#<=>https://bugs.ruby-lang.org/issues/1697?journal_id=64592009-10-26T21:14:08Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<ul><li><strong>Status</strong> changed from <i>Closed</i> to <i>Open</i></li></ul><p>=begin<br>
Hi Matz,</p>
<p>In rb_obj_cmp, the rb_obj_equal is redundant. Did you mean the following?</p>
<p>diff --git a/object.c b/object.c<br>
index 9f7537f..8f20621 100644<br>
--- a/object.c<br>
+++ b/object.c<br>
@@ -1123,7 +1123,7 @@ rb_obj_not_match(VALUE obj1, VALUE obj2)<br>
static VALUE<br>
rb_obj_cmp(VALUE obj1, VALUE obj2)<br>
{</p>
<ul>
<li>if (obj1 == obj2 || rb_obj_equal(obj1, obj2))</li>
</ul>
<ul>
<li>if (rb_equal(obj1, obj2))<br>
return INT2FIX(0);<br>
return Qnil;<br>
}</li>
</ul>
<p>=end</p> Ruby master - Feature #1697: Object#<=>https://bugs.ruby-lang.org/issues/1697?journal_id=64622009-10-26T22:24:31Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul></ul><p>=begin<br>
Hi,</p>
<p>In message "Re: <a href="https://blade.ruby-lang.org/ruby-core/26318">[ruby-core:26318]</a> <a href="Open">Feature #1697</a> Object#<=>"<br>
on Mon, 26 Oct 2009 21:14:08 +0900, Marc-Andre Lafortune <a href="mailto:redmine@ruby-lang.org" class="email">redmine@ruby-lang.org</a> writes:</p>
<p>|In rb_obj_cmp, the rb_obj_equal is redundant. Did you mean the following?</p>
<p>It's a grain of optimization to reduce function call.</p>
<pre><code> matz.
</code></pre>
<p>=end</p> Ruby master - Feature #1697: Object#<=>https://bugs.ruby-lang.org/issues/1697?journal_id=64632009-10-26T22:49:07Zrue (Eero Saynatkari)
<ul></ul><p>=begin<br>
Excerpts from Yukihiro Matsumoto's message of Mon Oct 26 15:24:12 +0200 2009:</p>
<blockquote>
<p>Hi,</p>
<p>In message "Re: <a href="https://blade.ruby-lang.org/ruby-core/26318">[ruby-core:26318]</a> <a href="Open">Feature #1697</a> Object#<=>"<br>
on Mon, 26 Oct 2009 21:14:08 +0900, Marc-Andre Lafortune <a href="mailto:redmine@ruby-lang.org" class="email">redmine@ruby-lang.org</a> writes:</p>
<p>|In rb_obj_cmp, the rb_obj_equal is redundant. Did you mean the following?</p>
<p>It's a grain of optimization to reduce function call.</p>
</blockquote>
<p>rb_obj_equal() is implemented just as ==, though, so it is<br>
redundant unless I am missing something? I would assume that<br>
the intended function was rb_eql() instead?</p>
<p>Marc-Andre's rb_equal() suggestion combines the two, although<br>
it uses id_eq, not id_eql, so I can see where avoiding that<br>
would be slightly better in performance.</p>
<p>Wishing there were fewer equality functions<br>
or at least more variance in their names,<br>
Eero</p>
<p>--<br>
Magic is insufficiently advanced technology.</p>
<p>=end</p> Ruby master - Feature #1697: Object#<=>https://bugs.ruby-lang.org/issues/1697?journal_id=64642009-10-27T01:23:05Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<ul></ul><p>=begin<br>
Given your comment, and Eero rightly pointing out that it's better to use eql? than == for this case,</p>
<p>diff --git a/object.c b/object.c<br>
index 9f7537f..8f20621 100644<br>
--- a/object.c<br>
+++ b/object.c<br>
@@ -1123,7 +1123,7 @@ rb_obj_not_match(VALUE obj1, VALUE obj2)<br>
static VALUE<br>
rb_obj_cmp(VALUE obj1, VALUE obj2)<br>
{</p>
<ul>
<li>if (obj1 == obj2 || rb_obj_equal(obj1, obj2))</li>
</ul>
<ul>
<li>if (obj1 == obj2 || rb_eql(obj1, obj2))<br>
return INT2FIX(0);<br>
return Qnil;<br>
}</li>
</ul>
<p>Note: I'm not saying that obj1 == obj2 is redundant. I'm saying rb_obj_equal is.</p>
<p>=end</p> Ruby master - Feature #1697: Object#<=>https://bugs.ruby-lang.org/issues/1697?journal_id=74212009-12-24T07:15:08Zujihisa (Tatsuhiro Ujihisa)
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Assigned</i></li></ul><p>=begin<br>
As Marc-Andre says, the current following code is redundant.</p>
<pre><code> static VALUE
rb_obj_cmp(VALUE obj1, VALUE obj2)
{
if (obj1 == obj2 || rb_obj_equal(obj1, obj2))
return INT2FIX(0);
return Qnil;
}
</code></pre>
<p>where</p>
<pre><code> VALUE
rb_obj_equal(VALUE obj1, VALUE obj2)
{
if (obj1 == obj2) return Qtrue;
return Qfalse;
}
</code></pre>
<p>in short, the original code means</p>
<pre><code> static VALUE
rb_obj_cmp(VALUE obj1, VALUE obj2)
{
if (obj1 == obj2 || ((obj1 == obj2) ? Qtrue : Qfalse))
return INT2FIX(0);
return Qnil;
}
</code></pre>
<p>As Marc-Andre says, there are two alternatives which are better than current code. If you prefer to allow Ruby to change the behavior of <code>Object#<=></code> by overwriting <code>Object#==</code>, apply the patch he showed. (Current RubySpec is based on it)</p>
<p>Otherwise, how about applying this patch:</p>
<pre><code> diff --git a/object.c b/object.c
index 503a7c5..3dd3290 100644
--- a/object.c
+++ b/object.c
@@ -1131,7 +1131,7 @@ rb_obj_not_match(VALUE obj1, VALUE obj2)
static VALUE
rb_obj_cmp(VALUE obj1, VALUE obj2)
{
- if (obj1 == obj2 || rb_obj_equal(obj1, obj2))
+ if (obj1 == obj2)
return INT2FIX(0);
return Qnil;
}
</code></pre>
<p>I'll fix the corresponding RubySpec after you decided which behavior is preferable.</p>
<p>=end</p> Ruby master - Feature #1697: Object#<=>https://bugs.ruby-lang.org/issues/1697?journal_id=74252009-12-24T09:31:56Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul></ul><p>=begin<br>
Hi,</p>
<p>In message "Re: <a href="https://blade.ruby-lang.org/ruby-core/27300">[ruby-core:27300]</a> <a href="Assigned">Feature #1697</a> Object#<=>"<br>
on Thu, 24 Dec 2009 07:15:13 +0900, "ujihisa ." <a href="mailto:redmine@ruby-lang.org" class="email">redmine@ruby-lang.org</a> writes:<br>
|<br>
|Issue <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Object#<=> (Closed)" href="https://bugs.ruby-lang.org/issues/1697">#1697</a> has been updated by ujihisa ..<br>
|<br>
|Status changed from Open to Assigned<br>
|<br>
|As Marc-Andre says, the current following code is redundant.<br>
|<br>
| static VALUE<br>
| rb_obj_cmp(VALUE obj1, VALUE obj2)<br>
| {<br>
| if (obj1 == obj2 || rb_obj_equal(obj1, obj2))<br>
| return INT2FIX(0);<br>
| return Qnil;<br>
| }</p>
<p>It should be:</p>
<p>| if (obj1 == obj2 || rb_equal(obj1, obj2))</p>
<pre><code> matz.
</code></pre>
<p>=end</p> Ruby master - Feature #1697: Object#<=>https://bugs.ruby-lang.org/issues/1697?journal_id=74262009-12-24T10:37:05Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<ul><li><strong>Status</strong> changed from <i>Assigned</i> to <i>Closed</i></li></ul><p>=begin<br>
This issue was solved with changeset r26161.<br>
Marc-Andre, thank you for reporting this issue.<br>
Your contribution to Ruby is greatly appreciated.<br>
May Ruby be with you.</p>
<p>=end</p>