Compare Patch

Following on from the discussions about the <=> operator and the Range

operator. The situation in the MRI is not consistent. There are some
cases
where an exception is thrown and some where nil is returned and in those
cases sometimes this is converted to false. Matz accepted this a while
back
(HYPERLINK
"http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/165155"http://b
lade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/165155) and I don’t
see
any change in the later releases.

Here is a quick patch I knocked up that tries to map to the MRI
behaviour.

The FixnumOps now has an overload on Compare that can take an object on
the
rhs and returns an object rather than int. This allows it to return
null if
the value is not compatible.

The Protocols.Compare now can return null if the comparison was not
valid.
There is a new Protocols.CheckedCompare that will return an int (as
before)
or throw an ArgumentException otherwise.

The RangeOps.CaseEquals now checks for null from the <=> operator and
returns false in that case.

This had a knock on effect across a number of classes including
Comparable,
IListOps, ArrayOps and Enumerable, which have been changed either to
explicitly cast to int (since it is guaranteed that it will be
compartible)
or to call CheckedCompare instead of Compare.

Pete

No virus found in this outgoing message.
Checked by AVG Free Edition.
Version: 7.5.503 / Virus Database: 269.15.29/1124 - Release Date:
11/11/2007
10:12

Peter Bacon D. wrote:

Following on from the discussions about the <=> operator and the Range
=== operator. The situation in the MRI is not consistent. There are
some cases where an exception is thrown and some where nil is returned
and in those cases sometimes this is converted to false. Matz accepted
this a while back
(http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/165155) and
I don’t see any change in the later releases.

If you’re going through the effort of finding all cases, please also try
to update either the Rubinius specs or RubySpec
(www.headius.com/rubyspec) so the information is not lost (or buried in
code).

This goes for other discoveries…please try to capture it through one
of the community-based mechanisms.

  • Charlie

Will do. Can anyone explain the relationship between Rubinius and
RubySpec
and the stuff in the test folder of IronRuby. There is clearly overlap
but
is one regularly updated from the other or what?
Pete

Peter Bacon D.:

Will do. Can anyone explain the relationship between Rubinius and
RubySpec
and the stuff in the test folder of IronRuby. There is clearly overlap
but
is one regularly updated from the other or what?

There is a directory in the Rubinius distribution called spec. That’s
where the RSpec tests live today. I did an early integration into our
tests directory, but they’re mixed in with other tests which makes it
hard to sync regularly with Rubinius. This week I’m going to move those
tests into their own directory (\tests\specs).

After that we’ll rendezvous with Rubinius specs on a regular basis.

-John

I have just realised that the overload I created in FixnumOps is
nonsense,
sorry:

    [RubyMethod("<=>")]

    public static object Compare(int lhs, int rhs)

    {

        return Compare(lhs, (int)rhs);

    }



    [RubyMethod("<=>")]

    public static object Compare(int lhs, object rhs)

    {

        if (rhs is int)

        {

            return Compare(lhs, (int)rhs);

        }

        return null;

    }

Since there is already an overload that takes int rhs then the second
overload will never get called with an int. Therefore the first three
lines
of the method are never needed. That leaves this method looking like:

    [RubyMethod("<=>")]

    public static object Compare(int lhs, object rhs)

    {

        return null;

    }

This then begs the question of is there a better way to pick up default
cases like this at binding time?

There is also a need for other overloads such as

    [RubyMethod("<=>")]

    public static int Compare(int lhs, double rhs)

    {

        return Convert.ToDouble(lhs).CompareTo(rhs);

    }

But again is there a more generic method of coercion for these types.
The
binder already attempts to do convert parameters but in this case the
conversion is the reverse (in effect converting the value of self rather
than rhs).

Other implementations talk about a coerce framework but this is offered
due
to a lack of method overloading based on types in Ruby. I assume that
the
way the binding works actually encourages such overloads in IronRuby.

Again sorry if I am being dumb.

Pete

No virus found in this outgoing message.
Checked by AVG Free Edition.
Version: 7.5.503 / Virus Database: 269.15.29/1124 - Release Date:
11/11/2007
10:12

No virus found in this outgoing message.
Checked by AVG Free Edition.
Version: 7.5.503 / Virus Database: 269.15.29/1124 - Release Date:
11/11/2007
10:12

For now, use overloads for method specialization on parameter type.
We might do some implicit conversions (e.g. MutableString ↔ CLR
string) in the binder but such conversions need to be general, not
specific to any operator/method.
I’m also thinking about adding support for “overloading by type and
protocol” (as opposed to type only). I.e. instead of this

public static void Foo(object obj) {
if (RubySites.RespondsTo(obj, “bar”) { … 1 …} else
if (RubySites.RespondsTo(obj, “baz”) {… 2 …} else { … 3 …}
}

one would just write (declaratively):

public static void FooForBar([HasMethod(“bar”)]object obj) {
… 1 …
}

public static void FooForBaz([HasMethod(“baz”)]object obj) {
… 2 …
}

public static void Foo(object obj) {
… 3 …
}

But it’s not a priority right now. We’ll see.

Tomas

From: [email protected]
[mailto:[email protected]] On Behalf Of Peter Bacon
Darwin
Sent: Monday, November 12, 2007 5:55 AM
To: [email protected]
Subject: Re: [Ironruby-core] Compare Patch

I have just realised that the overload I created in FixnumOps is
nonsense, sorry:

    [RubyMethod("<=>")]
    public static object Compare(int lhs, int rhs)
    {
        return Compare(lhs, (int)rhs);
    }

    [RubyMethod("<=>")]
    public static object Compare(int lhs, object rhs)
    {
        if (rhs is int)
        {
            return Compare(lhs, (int)rhs);
        }
        return null;
    }

Since there is already an overload that takes int rhs then the second
overload will never get called with an int. Therefore the first three
lines of the method are never needed. That leaves this method looking
like:

    [RubyMethod("<=>")]
    public static object Compare(int lhs, object rhs)
    {
        return null;
    }

This then begs the question of is there a better way to pick up default
cases like this at binding time?

There is also a need for other overloads such as

    [RubyMethod("<=>")]
    public static int Compare(int lhs, double rhs)
    {
        return Convert.ToDouble(lhs).CompareTo(rhs);
    }

But again is there a more generic method of coercion for these types.
The binder already attempts to do convert parameters but in this case
the conversion is the reverse (in effect converting the value of self
rather than rhs).
Other implementations talk about a coerce framework but this is offered
due to a lack of method overloading based on types in Ruby. I assume
that the way the binding works actually encourages such overloads in
IronRuby.

Again sorry if I am being dumb.

Pete

No virus found in this outgoing message.
Checked by AVG Free Edition.
Version: 7.5.503 / Virus Database: 269.15.29/1124 - Release Date:
11/11/2007 10:12

No virus found in this outgoing message.
Checked by AVG Free Edition.
Version: 7.5.503 / Virus Database: 269.15.29/1124 - Release Date:
11/11/2007 10:12

That would be interesting and very duck-like. From looking at ruby doc
it
appears that the Numeric class implements <=> such that it either
returns 0
(if both sides are equal) or nil. Pushing my method up to Numeric and
implementing it properly makes more sense.

Pete