Operator overloading in Scala and Kotlin: two slightly different ways

Photo by Crissy Jarvis on Unsplash

As you probably know, Java does not allow user-defined operator overloading, which is the ability to define the arithmetic operators (like + and *) for types other than the Java Virtual Machine (JVM) numeric primitives.

Scala does have operator overloading, or, at least, something that in practical terms is pretty much the same thing: you can use the arithmetic operators as function names.

If Scala has a feature, Kotlin probably has that same feature, too, though it probably uses a slightly different syntax to hide the fact that the idea came from Scala.

Operator overloading is a feature you might not care about if you feel the primitive numeric data types in the JVM are more than enough for all your number-crunching needs.

Though of course operator overloading is not limited to numeric data types. You could very well choose to define the bitwise left shift operator (<<) to add a String to an output stream — you know, like in C++?

It looks like all of Scala’s collection classes have at least a few functions named with operator symbols. So there is plenty of precedent if you wish to use operator symbols for your own non-numeric data types.

However, it is for your custom numeric data types that operator overloading makes the most sense, in my opinion.

For example, if you need a data type for algebraic integers like √2, or imaginary numbers like i (note that the double Math.sqrt(2) is a rational floating point approximation to √2; while purely imaginary numbers are altogether outside the scope ofdouble).

So, if we were to define a class to represent algebraic integers, or a class to represent complex numbers like √i, it would make sense to use operator overloading to make the formulas in our programs look a bit more like mathematical formulas.

A better example, though, would be a class to represent fractions like 1/2 and 3/4. In Java, we can certainly define a Fraction class that is constructed with an integer numerator and a nonzero integer denominator.

The main benefit of the Fraction class, I think, is postponing the use of floating point arithmetic until it’s strictly necessary, thus avoiding the accumulation of small errors.

This Fraction class would also provide conveniences like automatically putting fractions in lowest terms even if the constructor doesn’t get the numerator and denominator in lowest terms, e.g., new Fraction(70, 50).

But what about adding, subtracting, etc.? We’d have to use function calls.

It would be nice if, for instance, we could write something like “oneHalf + oneThird” instead of “oneHalf.plus(oneThird).” In Scala, we can.

Part of what makes that possible is that the Scala compiler will understand infix notation (ditching the parentheses)for any instance function that takes a single parameter.

So, if we have a Fraction class written up in Java with the basic arithmetic functions, we can use it from a Scala class like this:

The other part of it is that in Scala we have a somewhat larger repertoire of characters for function names than we do in Java. So we can define, for example, in the Scala Fraction class,

Certainly we could call this as, say, “oneHalf.+(oneThird),” if we really wanted to. But the whole point here is to call it as “oneHalf + oneThird,” thus using a syntax that feels more natural.

Of course the JVM does not allow the plus symbol in identifiers. But it does allow the dollar sign. So what the Scala compiler does is compile the + function as $plus, which, for all I know, could be the name the of a dollar store near Silicon Valley.

We can use such a Scala class from a Java class, but in the Java class we’d need to use the identifier with the dollar sign, e.g., “oneHalf.$plus(oneThird).” Of course in other Scala classes we can use the plus sign infix syntax.

For the unary negation operator, there is a small wrinkle. I mistakenly thought that it would be sufficient to define - with no parameters, and then the Scala compiler would be able to distinguish it from - with a subtrahend parameter. That’s not the case at all.

What you have to do is define it as unary_-, so that then the Scala compiler understands this is a prefix symbol.

Then it becomes immediately available for use in our “binary” - function:

It is my understanding that there are only three other symbols that are available as prefix operators in Scala, and Scala inventor Martin Odersky doesn’t want to add any more of them.

Put in an implicit conversion from Int to Fraction, and the Fraction class becomes really useful at the Scala REPL, which starts to feel a little bit like a Mathematica notebook.

I suppose that in an IDE, with all the auto-complete help, operator overloading is not that big a deal. It still pays off in subtle ways, though. I think it’s worthwhile to implement even if you don’t use the local Scala REPL.

If you have Fraction implement the Ordered[A] trait (which extends java.lang.Comparable[A]), then you can also write things like “if (someFraction > otherFraction)” rather than the somewhat clunky “if (someFraction.compareTo(otherFraction) > 0).”

I hinted earlier that Kotlin takes Scala features but implements them differently just for the sake of not being exactly like Scala. That’s certainly the impression I get from Kotlin’s when statements (compare Scala match statements, both are touted as an improvement on Java switch statements).

However, the Kotlin designers might have had a legitimate objection to the way Scala does operator overloading. The objection, I think, pertains to Java interoperability.

Having to type “$plus” doesn’t seem so bad. For unary negation, though, “unary_$minus” feels like the opposite of a shortcut, and really makes you appreciate an IDE’s auto-complete.

To say nothing of “$greater$eq” (if Fraction implements the Ordered[A] trait). Though I suppose in that case you’d rather use compareTo() anyway.

Kotlin takes a different approach. We can also write things like “oneHalf + oneThird,” but we don’t define +(). Instead, we define plus() with the operator modifier.

And instead of defining unary_-, we define unaryMinus().

Then these operators are available for use in our “binary” minus() function.

The advantage of this approach becomes apparent when interoperating with Java classes. Theoretically I could bring EgyptianFractionViewer.java into the project and have it use Fraction.kt instead of Fraction.java without any problems.

In practice, I did have a few problems, the most annoying of which was that I can’t use the default parameter (for when denominator can be understood to be 1, making the fraction an integer) from a Java class, unless the Kotlin class has an auxiliary (likely chained) constructor to fill in the default parameter.

A little reflection will readily show why this is the case. The default parameter syntax in Scala and Kotlin is a convenience which neither the Scala compiler nor the Kotlin compiler can force on the Java compiler.

But the arithmetic function calls were no problem at all. Obviously I can’t use the operator forms from a Java class, but I didn’t have to rename anything from Fraction.kt in a Java class (the Egyptian Fraction Viewer program only uses fraction addition, subtraction and comparison).

Though I suppose that with Fraction.scala, it wouldn’t be too big a deal to mass replace in EgyptianFractionViewer.java each occurrence of “.plus” with “.$plus,” “.minus” with “.$minus,” etc.

Kotlin has its own version of Scala’s Ordered<T>, but it’s surprisingly called Comparable<T>. There’s no risk of confusion with java.lang.Comparable<T>, since you’d have to explicitly import that one if that’s the one you want.

However, the Boolean functions greater(), greaterEq(), less(), lessEq() are unavailable to Java classes, unlike $greater(), $greater$eq(), $less(), $less$eq() would be from a Scala class to a Java class. No big loss there.

One of the arguments given against operator overloading is that it can lead to meaningless identifiers. But, as Cay Horstmann points out in Scala for the Impatient, it’s always possible to give things meaningless names even when we’re limited to ASCII letters.

Granted that it is possible with operator overloading to give things counter-intuitive names, such as calling “+” a function that really should be called “removeAll(),” or perhaps worse, “remove().”

Ultimately, though, the designers of Scala and Kotlin had greater faith than the designers of Java that programmers will only use operator overloading when it is appropriate, and in ways that make sense.

is a composer and photographer from Detroit, Michigan. He has been working on a Java program to display certain mathematical diagrams.