[Ironruby-core] Range#=== operator

John Messerly jomes at microsoft.com
Thu Nov 8 16:55:15 EST 2007

Curt Hagenlocher wrote:

> On 11/8/07, Peter Bacon Darwin <bacondarwin at googlemail.com> wrote:
>>      One other question though:  How do you decide whether to use dynamic
>> sites to trigger code rather than calling code directly when in coding
>> in C#.  For instance, why does Protocols.Compare invoke
>> _CompareSharedSite rather than call a C# function directly?  Sorry if
>> I am being dumb.
> Even builtins like the String or Range classes can have their
> comparison functions overridden by user code.  So you really need to
> perform the comparison "in Ruby".

Exactly. The trick with library methods is to figure out what pattern MRI uses. Typically, they do a method lookup on the type, which in our world translates into a DynamicSite. However there are some places where they just call some library method directly and bypass any overridden/monkeypatched method. So it requires some experimentation :).

Protocols.Compare is one of the stranger things I've discovered. MRI will call <=>, and then call <  and > on the result object... which of course can be overridden to do whatever you want. Because Compare is kind of crazy I'm not surprised we get it slightly wrong.

Peter Bacon Darwin wrote:

> By the way, a simple way to get the desired results for the case
> equality issue mentioned before is to put a try/catch around the
> Compare and return false if an ArgumentException is thrown.  See the
> attached patch.  Is this too much of a hack or acceptable?

Peter, I think the right fix here is for Fixnum <=> to return nil if it gets passed a non-comparable object. That seems to be the standard MRI behavior for <=>:

irb(main):013:0> (1..5) === 'x'
=> false

And then I think our Protocols.Compare needs to check if <=> returned null, and not call > or < if that was the case. Indeed, if you define '<' and '>' on NilClass it doesn't change the compare behavior:

irb(main):014:0> class NilClass
irb(main):015:1> def < x; puts '<'; 0; end
irb(main):016:1> def > x; puts '>'; 0; end
irb(main):017:1> end
=> nil
irb(main):018:0> (1..5) === 'x'
=> false

Of course, it might require more experiments to get it exactly right for all cases.

- John

More information about the Ironruby-core mailing list