Why Everyone Hates Operator Overloading


… no, don’t tell me you like Perl. Because you don’t. You never did. It does horrible things. It makes your code look like…

Perl made heavy use of operator overloading and used operators for a variety of things. A similar tendency can be seen in C++ and Scala. See also people comparing the two. So what’s wrong with operator overloading?

People never agreed whether Scala got operator overloading right or wrong:

Usually, people then cite the usual suspects, such as complex numbers (getting things right):

class Complex(val real:Int, 
              val imaginary:Int) {
    def +(operand:Complex):Complex = {
        new Complex(real + operand.real, 
                    imaginary + operand.imaginary)
    }
 
    def *(operand:Complex):Complex = {
        new Complex(real * operand.real - 
                    imaginary * operand.imaginary,
            real * operand.imaginary + 
            imaginary * operand.real)
    }
}

The above will now allow for adding and multiplying complex numbers, and there’s absolutely nothing wrong with that:

val c1 = new Complex(1, 2)
val c2 = new Complex(2, -3)
val c3 = c1 + c2
 
val res = c1 + c2 * c3

But then, there are these weirdo punctuation things that make average programmers simply go mad:

 ->
 ||=
 ++=
 <=
 _._
 ::
 :+=

Don’t believe it? Check out this graph library!

To the above, we say:

Operator Overloading? Meh

How operator overloading should be

Operator overloading can be good, but mostly isn’t. In Java, we’re all missing better ways to interact with BigDecimal and similar types:

// How it is:
bigdecimal1.add(bigdecimal2.multiply(bigdecimal3));

// How it should be:
bigdecimal1 + bigdecimal2 * bigdecimal3

Of course, operator precedence would take place as expected. Unlike C++ or Scala, ideal operator overloading would simply map common operators to common method names. Nothing more. No one really wants API developers to come up with fancy ##-%>> operators.

While Ceylon, Groovy, and Xtend implemented this in a somewhat predictable and useful way, Kotlin is probably the language that has implemented the best standard operator overloading mechanism into their language. Their documentation states:

Binary operations

Expression Translated to
a + b a.plus(b)
a – b a.minus(b)
a * b a.times(b)
a / b a.div(b)
a % b a.mod(b)
a..b a.rangeTo(b)

That looks pretty straightforward. Now check this out:

“Array” access

Symbol Translated to
a[i] a.get(i)
a[i, j] a.get(i, j)
a[i_1, ..., i_n] a.get(i_1, …, i_n)
a[i] = b a.set(i, b)
a[i, j] = b a.set(i, j, b)
a[i_1, ..., i_n] = b a.set(i_1, …, i_n, b)

Now, I really don’t see a single argument against the above. This goes on, and unfortunately, Java 8 has missed this train, as method references cannot be assigned to variables and invoked like JavaScript functions (although, that’s not too late for Java 9+):

Method calls

Symbol Translated to
a(i) a.invoke(i)
a(i, j) a.invoke(i, j)
a(i_1, …, i_n) a.invoke(i_1, …, i_n)

Simply beautiful!

Conclusion

We’ve recently blogged about Ceylon’s awesome language features. But the above Kotlin features are definitely a killer and would remove any other sorts of desires to introduce operator overloading in Java for good.

Let’s hope future Java versions take inspiration from Kotlin, a language that got operator overloading right.

Tags: , , , , , ,

18 responses to “Why Everyone Hates Operator Overloading”

  1. Simon says :

    2/3 looks pretty much like what’s shipped in Scala since pretty much forever, just a bit renamed, because NIH I guess.

    Regarding the first one … imho, “operator overloading” is just not important enough to bother coming up with elaborate rules about how things are translated from symbols to names.

    It’s kind of funny to see that there is pretty much no intersection between actual users of Scala and people obsessed with complaining about symbolic method names.

    • lukaseder says :

      Regarding the first one

      What do you mean by “the first one”?

      [...] obsessed with complaining [...]

      This was the first article on this blog related to operator overloading / symbolic method names, and it wasn’t really Scala-centric. People have criticised C++ for similar reasons, and for precisely these reasons, operator overloading was never part of the JLS. Or do you mean someone else when you say “obsessed”? Or is this the usual reaction people get when they think 1-2 things are not OK with a particular language? :-)

      • Simon says :

        > What do you mean by “the first one”?

        I meant “Binary operations”.

        > Or is this the usual reaction people get when they think 1-2 things are not OK with a particular language?

        No, sorry, that wasn’t targeted against you or your blog post, just wanted to share my experience. :-) My perception is that these things are big deal for excatly those who have never used that functionality in the first place.

        • lukaseder says :

          I meant “Binary operations”.

          Scala does that too, automatically by convention? I didn’t know that! Nice.

          My perception is that these things are big deal for excatly those who have never used that functionality in the first place.

          Aha, I see. Anything can be a big deal if you have time and passion for the details :-) Some call it the bikeshed problem. Another example is JavaScript. Recently, I’ve posted this article to Reddit:

          http://www.reddit.com/r/programming/comments/1xfh9w/why_you_should_always_use_and_other_bad_practices/

          It’s one of my most popular posts ;-)

          • Simon says :

            > Scala does that too, automatically by convention? I didn’t know that! Nice.

            No, ““Array” access” and “Method calls” are pretty much identical, just with other names and no special [] operators/syntax.

            I’d say that “Binary operations” is a pretty bad idea.

            Pretty much every language invented on this planet has the intuitive rule of “if you name a method X, you can invoke that method using that name”.

            I’m not seeing the benefit of the approach of “we have this hardcoded list of special method names X, Y and Z, which you are supposed to invoke by using foo, bar and baz”.

            What’s the benefit of that?
            To allow people to freely mix those names like

            val a = 2
            val b = 3
            val c = 5
            a.modAssign(c + (b.times(a) / b.minus())

            ?

            I think the simpler approach of “Use the method name you want, if it looks like math the known precedence rules apply, done.” is better thought out.

            Just imagine writing a language spec on http://confluence.jetbrains.com/display/Kotlin/Operator+overloading … I don’t want to imagine how many pages one will waste on something which should only be used very sparingly anyway.

          • lukaseder says :

            Pretty much every language invented on this planet has the intuitive rule of “if you name a method X, you can invoke that method using that name”.

            Well, there are exceptions:

            var foo = new function() {};
            var bar = foo;
            
            bar('abc'); // This calls foo
            

            Just imagine writing a language spec [...]

            This appears to be the “spec”: http://confluence.jetbrains.com/display/Kotlin/Grammar. It looks as though these math operators will only work on the same type, i.e. A = A * A. Removing Java’s primitive type promotion is already greatly helping in formalising this type of convention.

            From a Specs point of view, I guess Scala can do away with processing the AST several times to first substitute what looks like math by method calls with appropriate precedence, before compiling the actual methods. But then again, this additional processing step slows down compiling…

          • Simon says :

            > Well, there are exceptions:

            I don’t think this is an exception, in fact most languages allow this. Note that you can still use foo(), but if you define the method “timesAssign”, you are not supposed to invoke it as “timesAssign”, but as something named differently in Kotlin.

            Regarding the spec: The grammar is the least important and interesting detail of a language spec. What matters is the description of semantics and with that list of hard-coded special cases …

            Regarding processing the AST in Scala: This is all handled in a single phase called parser. The reason why Scala uses multiple phases is because it makes it more easy to verify that each transformation is done correctly and allows people to more easily extend the compiler.

            Anyway, looking at performance, parsing pretty much doesn’t matter at all. In a sense, pretty much no phase matters from a performance POV except typechecking and codegeneration.
            There is already a new codegeneration backend which improves performance substantially, so typechecking is pretty much it, and it’s hard to optimize it, when other things (like correctness) are much more important.

            I think performance of typechecking could increase considerably if Hotspot would do its job properly, instead of letting scalac run in interpreted mode for the first 2 minutes. By the time it decides to emit substantial amounts of JIT-compiled code, the compilation is likely to be done already.

          • lukaseder says :

            Alright, thanks for all this information! I learned a couple of things

  2. Luke says :

    The Kotlin documentation is unclear about how somebody writing custom Kotlin code would implement a custom overloaded operator. Is there a convention that a method “plus()” would translate to “+”? If so, how is this different from Ceylon’s polymorphic operator overloading, where you need to satisfy the Summable interface?

  3. Luke says :

    Sorry… misunderstood you.

    On a lighter note, here’s an example of operator overloading abuse :)

    http://www.scala-graph.org/guides/core-initializing.html

  4. Jan Friderici (@jnfrd) says :

    1) You can not overload operators in Scala – you can only use non asci characters in methods. That is a huge difference.
    2) I have seen a lot of abuse of extends in java – should we forbid it because some people are to stupid to use it correctly?

    • lukaseder says :

      I can see your point 2). Every tool can be abused, although “extends” clearly pulls its weight. About 1), technically you’re correct. You can use non-ascii characters and leave away syntactic elements like dots and parentheses. When I read / maintain legacy code, however, that distinction might seem a bit academic.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 1,738 other followers

%d bloggers like this: