New operators

I think that having three different equals operators in the language runs very much against Kotlin’s ambitions to be clear and simple.

@yole But the real problem is not having three different operators, but having three different notions of equality. And this is already the case and can’t be changed. Whenever there’s something like >=, then a corresponding equality operator should be provided. As the consistency of compareTo and equals can’t be enforced, the induced equalities will differ.

Given three notions of equality, having three corresponding operators is the simplest solution.

@nbengtg To me, recycling = for equality is worse than using a new third operator, assuming we can find something readable. For “not equal according to compareTo”, <> is the obvious choice, with the mnemonics of “compareTo returns something < or > than zero” or simply “less or bigger”. For the opposite, I’d suggest !<> which reads as “neither less nor bigger”. This way, all operators based on compareTo contain some < or >, which makes it pretty clear.

@Dmitry_Petrov_1 You’re surely aware that int -> float and long -> double are lossy and you won’t repeat the Java mistake to call them widening, will you? The only really widening conversions between integral and floating-point types in Java are byte/short -> float and byte/short/int -> double.

1 Like

I like <> (and !<>) operators. They look funny, it means that reader will notice them. It’s a good idea, if a problem is real (honestly I can’t imagine the situation where I would want to use it, but my imagination might be limited).

What I don’t like is operator ===. It shouldn’t exist. Problem with Java’s == is that it means something different for referential types and it was easy to compare things like strings or boxed numbers and sometimes it would even work. Kotlin solved this problem, but === is easy to mistype when you wanted to type ==, it’s hard to spot, it will work sometimes and surely it’ll introduce subtle bugs. It should be replaced by something like x referentiallyEquals y.

The fact that some classes implement compareTo() inconsistently with equals() is a corner case, and is important only in extremely rare cases. Having three different kinds of comparison operators in the language is a very visible feature, and will be greatly confusing to all learners of Kotlin, including those who will never in their life encounter a situation where the difference matters.

1 Like

@yole I don’t find it confusing at all, but people who didn’t read this discussion probably do, especially programming beginners. It’s true that the difference matters only very rarely. So I agree with you.

Another thing is the comparison of different numeric types. I guess, there’s no chance of making the above example

val a = 1
val b = 1L
a >= b gives true
a == b is not testable

work for == as it’s bound to be Java equals and it’s already defined for all operand combinations. So you can’t make 1 == BigInteger(1) work, but you can make 1 >= BigInteger(1) return true by providing an overload for compareTo. Now we have a much better use case for <> and !<> as they can use the overload. You can make it work for all combinations of predefined Kotlin data types and users can extend it for their own types.

@maaartinus This is exactly my point. @yole I don’t think this is a corner case. As I said above in reply to

Looks like this problem can be solved much more easily by providing a different implementation of BigDecimal

equals() and compareTo serve different purposes and are implemented differently. Specifically, I can implement compareTo(myType) for any numerical type by an extension function, but I can not extend equals() as it is defined on Any. It’s not BigDecimal that is the problem, it also goes for any custom numerical type.

As for this being important only in extremely rare cases, that, I submit, depends on what areas of programming Kotlin is being used for, which, conversely, will be correlated to how well Kotlin is able to express what is needed in these areas. I am just now considering Kotlin for implementing a considerable amount of business logic. I had hoped that Kotlin would enable me to write more readable code, as compared to (!) Java, but if I can write > instead of compareTo()>0, but not = instead of compareTo()==0, it will not be clearer, just inconsistent, and we will probably go with Java anyway (or possibly Scala). Which is kind of sad, because I like Kotlin very much in many respects.

1 Like

@nbengtg I see, I was just repeating your arguments. That’s because of this thread being damn long and because of me being a Java guy, where compareTo is the way of implementing Comparable and nothing more.

I’m new to Kotlin and am having this problem with BigDecimal as it doesn’t like the fact that mathematically 0 and 0.0 are the same thing.

I understand that what our brain expects is not the same as what an object would be. It would be like saying a piece of paper with 0 written on it is exactly the same thing as a piece of paper with 0.0 written on it. They are not.

My point is, if you need numeric equality in your test, you need to transform both sides into the same thing, like you would test a String using equalsIgnoreCase() for instance.

My suggestions as a beginner, if people agree with me:

Use unscaledValue() in both sides of the test

or

Create your own extension functions for this equality check somewhat like this:

fun BigDecimal.equalsIgnoreScale(other: BigDecimal): Boolean {
    return this.unscaledValue() == other.unscaledValue()
}
fun BigDecimal.equalsIgnoreScale(other: Double): Boolean {
    return this.unscaledValue() == BigDecimal(other).unscaledValue()
}
.
.
.

Another option would be extension functions overwrite member functions with same signature. But that could cause some other problems, so it is a good thing that this is not possible.