## Bug #15857

### <=> の右辺が <=> を実装していない場合の振る舞い

**Description**

`<=>`

の右辺が `<=>`

を実装していないとき、`nil`

が返却される場合と例外が発生する場合があり一貫性がないように思えるのですが、意図的でしょうか。

```
0 <=> 0i #=> NoMethodError (undefined method `<=>' for (0+0i):Complex)
0 <=> BasicObject.new #=> nil
:a <=> 0i #=> nil
"a" <=> 0i #=> NoMethodError (undefined method `<=>' for (0+0i):Complex)
```

なお、`0 <=> 0i`

に関しては、`0 == 0i`

は `true`

になるのでそれとも一貫性がないように思えるのもやや気になりました。

**Files**

### Associated revisions

### History

#### Updated by jeremyevans0 (Jeremy Evans) 2 months ago

I agree that this is a bug. `Complex#<=>`

should be implemented, even if it did return `nil`

for all arguments.

However, I think Complex numbers with an imaginary part of zero should be treated as real numbers, and that `Complex#real?`

should return `true`

for such numbers. Likewise, `Complex#<=>`

should return 1, 0, or -1 if both the receiver and the argument are real numbers (treating Complex instances with an imaginary part of zero as real numbers). However, if the receiver or the argument is not a real number, then it should return `nil`

.

Attached is a patch that implements the behavior described above.

#### Updated by sawa (Tsuyoshi Sawada) 2 months ago

jeremyevans0 (Jeremy Evans) wrote:

I think [that] Complex numbers with an imaginary part of zero should be treated as real numbers, and that

`Complex#real?`

should return`true`

for such numbers.

Changing `Complex#real?`

's meaning has huge influence. You would also have to change `Numeric#integer?`

for consistency.

Notice that in Ruby, there is no class `Real`

, and `real?`

means "not an instance of `Complex`

". I believe `Float`

is an approximation of the concept of real numbers in mathematics, but unfortunately, `Integer`

and `Rational`

are not subclasses of `Float`

like integers and rational numbers are subsets of real numbers in the mathematical sense. So discussing your proposal in terms of real numbers does not look that helpful.

`Complex#<=>`

should return 1, 0, or -1 if both the receiver and the argument are real numbers (treating Complex instances with an imaginary part of zero as real numbers).

I think this should rather be handled by coercion as with arithmetic operations.

#### Updated by jeremyevans0 (Jeremy Evans) 2 months ago

sawa (Tsuyoshi Sawada) wrote:

jeremyevans0 (Jeremy Evans) wrote:

I think [that] Complex numbers with an imaginary part of zero should be treated as real numbers, and that

`Complex#real?`

should return`true`

for such numbers.Changing

`Complex#real?`

's meaning has huge influence. You would also have to change`Numeric#integer?`

for consistency.Notice that in Ruby, there is no class

`Real`

, and`real?`

means "not an instance of`Complex`

". I believe`Float`

is an approximation of the concept of real numbers in mathematics, but unfortunately,`Integer`

and`Rational`

are not subclasses of`Float`

like integers and rational numbers are subsets of real numbers in the mathematical sense. So discussing your proposal in terms of real numbers does not look that helpful.

Thank you for providing some background on the purpose of the `real?`

method. I agree that it doesn't make sense to change `real?`

. Attached is a patch that only implements `<=>`

and removes the modifications to `real?`

. It also adds specs for `<=>`

.

`Complex#<=>`

should return 1, 0, or -1 if both the receiver and the argument are real numbers (treating Complex instances with an imaginary part of zero as real numbers).I think this should rather be handled by coercion as with arithmetic operations.

Could you please provide a patch for your approach so we can compare and choose the simpler implementation?

#### Updated by shuujii (Shuji KOBAYASHI) 2 months ago

jeremyevans0 (Jeremy Evans) Thank you for your comment. About `Complex`

, I agree with most of v2 patch, but I have some comments.

- Should methods from
`Comparable`

that are currently disabled be enabled? `rb_undef_method(rb_cComplex, "<=>")`

is unneeded.`idCmp`

can be used instead of`id_spaceship`

.

About other than `Complex`

, `<=>`

of some classes, such as `String`

and `Time`

, will fail if ~~LHS~~RHS does not implement `<=>`

. It's better to return `nil`

in this case too, because whether it's comparable or not and whether ~~LHS~~RHS implements `<=>`

or not is essentially irrelevant, I think.

#### Updated by jeremyevans0 (Jeremy Evans) 2 months ago

shuujii (Shuji KOBAYASHI) wrote:

jeremyevans0 (Jeremy Evans) Thank you for your comment. About

`Complex`

, I agree with most of v2 patch, but I have some comments.

- Should methods from
`Comparable`

that are currently disabled be enabled?

I don't think so, but I don't have a strong opinion. If you would like those added, please submit a separate feature request for that.

`rb_undef_method(rb_cComplex, "<=>")`

is unneeded.`idCmp`

can be used instead of`id_spaceship`

.

Thank you, I made both of these changes in the attached v3 patch.

About other than

`Complex`

,`<=>`

of some classes, such as`String`

and`Time`

, will fail if LHS does not implement`<=>`

. It's better to return`nil`

in this case too, because whether it's comparable or not and whether LHS implements`<=>`

or not is essentially irrelevant, I think.

I agree, but please submit a separate bug report for that as it is unrelated to `Complex#<=>`

.

#### Updated by shuujii (Shuji KOBAYASHI) 2 months ago

jeremyevans0 (Jeremy Evans) Thank you for your comment. About Complex, I agree with most of v2 patch, but I have some comments.

- Should methods from Comparable that are currently disabled be enabled?
I don't think so, but I don't have a strong opinion. If you would like those added, please submit a separate feature request for that.

I don't have a strong opinion, too.

About other than Complex, <=> of some classes, such as String and Time, will fail if

~~LHS~~RHS does not implement <=>. It's better to return nil in this case too, because whether it's comparable or not and whether~~LHS~~RHS implements <=> or not is essentially irrelevant, I think.I agree, but please submit a separate bug report for that as it is unrelated to Complex#<=>.

Sure. I will create another ticket.

#### Updated by jeremyevans (Jeremy Evans) 2 months ago

**Status**changed from*Open*to*Closed*

Applied in changeset git|b9ef35e4c6325864e013ab6e45df6fe00f759a47.

Implement Complex#<=>

Implement Complex#<=> so that it is usable as an argument when

calling <=> on objects of other classes (since #coerce will coerce

such numbers to Complex). If the complex number has a zero imaginary

part, and the other argument is a real number (or complex number with

zero imaginary part), return -1, 0, or 1. Otherwise, return nil,

indicating the objects are not comparable.

Fixes [Bug #15857]

Implement Complex#<=>

Implement Complex#<=> so that it is usable as an argument when

calling <=> on objects of other classes (since #coerce will coerce

such numbers to Complex). If the complex number has a zero imaginary

part, and the other argument is a real number (or complex number with

zero imaginary part), return -1, 0, or 1. Otherwise, return nil,

indicating the objects are not comparable.

Fixes [Bug #15857]