x + y
x.+(y)
The root cause of this is the distinction between primitive and object types. Simply put, not everything is an object in Java and this causes numberless problems. One of them is the need for special syntax cases for the mathematical operators of the primitive types.Some Smalltalk methods are "compiled in-line" special —
ifTrue: alternativeBlock
"If the receiver is false (i.e., the condition is false), then the value is the
false alternative, which is nil. Otherwise answer the result of evaluating
the argument, alternativeBlock. Create an error notification if the
receiver is nonBoolean. Execution does not actually reach here because
the expression is compiled in-line."
self subclassResponsibility ifTrue:Operator overloading on a limited set on operators, like in Kotlin is fine.
Not that limitations are bad, Kotlin is just weird in them.
However, as a regular old user, operator overloading can be a drag. Let's say you come across the expression a + b. Are a and b numbers and are you adding them? Are they strings and are you concatenating them? Are they vectors and are you appending them? As a programmer, you don't know, so when you see this expression you have to go on a fact-finding mission to figure out what's going on. For a typed language like C++ this is not so bad; you just need to look up the types of a and b. But that isn't always straightforward due to type inference.
The fact is that the symbol + has a well-defined meaning in mathematics. Operator overloading takes a very clear and well-defined symbol and turns it into a fuzzy enigma. In my opinion, it's better to have N well-defined, clear, and unique semantics for N symbols in a PL rather than having a single symbol that is overloaded for N purposes. Writing the code may take longer, but code is read more than it's written, so that doesn't seem so bad to me.
One has to go through the same analysis the function called +.
+ can refer to different functions in math, such as addition of integers or matrices. Hell, number and matrix multiplication are written in the same way even though one is commutative and the other isn't!
So why is a.plus(b) better than a + b? What would be the problem of translating "a + b" automatically into a.plus(b) like how Kotlin does it?
All the arguments that I have ever seen against operator overloading are just various ways of formulating one single argument and this argument has nothing to do either with operators or with overloading.
The argument is that some programmers are stupid and they give names that are misleading, because those names mean something else than what most people would expect them to mean.
I agree that this is true, but it happens with any kinds of names, of variables, of functions, of macros and of course also with the operator symbols when it is allowed to define functions whose names are operator symbols.
I do not agree with this kind of arguments that some people are stupid and they might do something bad with a kind of tool so let's forbid to all people the right of carrying such a tool.
In my experience, the ability to understand complex programs depends a lot on whether you can see the entire relevant part of the code on the screen simultaneously. Few people have such a good memory that they are still able to understand the code if they are forced to flip constantly through multiple pages of text.
So there are places in a program where long descriptive names can make the program easier to read and understand, but there are also programs with complicated formulas, where a compact notation makes the code much easier to read and understand.
Many years ago, about at the same time when Java was launched, I worked at a program that computed the radiated electromagnetic field for some systems. The model required a large number of custom types corresponding to various physical quantities, and most of them had values that were vectors of complex numbers.
The code contained a large number of very complicated formulas, corresponding to multidimensional integrals of various functions.
The initial version had been written in C, and the text of the program was huge and extremely difficult to read and understand.
Rewriting it in C++ with overloaded operators resulted in a dramatic reduction in size and it became possible to understand what happens in the code by just reading it.
At the same time, the Java propaganda against overloaded operators began, but obviously, after my experience, I dismissed it as misguided.
I agree that C++ is not an example of how it is best to handle operator overloading, because C++ allows the overloading of some operators for which overloading should be forbidden, because the compiler should be able to generate them for any custom type in a way that would guarantee the expected operator properties and there is no advantage in allowing the programmer to implement those operators with functions having a different behavior.
While criticizing the excesses of C++ is fine, forbidding operator overloading completely makes a programming language unsuitable for some applications.
Operator overloading is nothing new, it has always existed for the base types provided by a language, since Fortran, then it was proposed for user-defined types in 1964 by John McCarthy and then it was allowed in various languages before C++, e.g. Algol 68 or Ada.
The aversion of the Java authors against operator overloading, which was very explicit since the first Java presentations, is quite weird because it is inconsistent with the Java love for OOP.
The essence of OOP is dynamic overloading caused by inheritance, which has the potential to be much more confusing than the static overloading of operators.
If I see an "object + object" I might not know what the "+" does, but I can find by searching in the source code. When I see an "object.plus()", then the possibility that the member function does something completely different than I expect is much higher, because the actual function that is invoked will be determined only at run time.