Scala Gets Operator Overloading Right

02/20/2010 Update: I took this blog post and turned it into a presentation to the Atlanta Scala User Group, which I gave in January 2010. The slides from that presentation are here.

05/31/2009 Update: As Mads Andersen pointed out, in the example below, I had the Complex multiplication wrong. As I was working through the example in the book, I typed it incorrectly. I could argue that this blog post was not about my mastery of complex numbers, and was actually meant to show how operator overloading works in Scala, but in the interest of all-around correctness, I have updated the code to contain the proper implementation of complex number multiplication, and I have worked out the examples by hand using the formula on the Wikipedia page to ensure their accuracy. Now, on to the original article.

I’ve been intrigued by Scala for a while now, and I’m finally taking the time to learn it. As I was reading in my book yesterday, I came to the section on operator overloading. Now, this is a topic that developers who’ve been exposed to it feel very strongly about. It’s not quite as rough a discussion as vi vs. Emacs, but it’s close. Some say that operator overloading makes for more elegant code. Some say it just confuses things. I’ve always been in the elegant camp. I think if you can provide operators for your own classes that work intuitively, you ought to be able to do it. In Java, think about how nice BigDecimal would be to use if you had + and – instead of add() and subtract(). Of course, as with anything of power, you have to be careful not to abuse it. It would make no sense to provide a + operator on a Date class, since adding two dates together makes no sense. You have to be smart about it, but having the ability to provide operators for your own classes performing intuitive functions is a good thing.

So as I’m reading the section on operator overloading, it was nice to see that even though you can override the standard mathematical operators, Scala still maintains the proper precedence that everyone is used to. By this I mean that a + b * c will execute the multiplication first, then add the product of b and c to a. The reason this is interesting is because another language that I still love, Smalltalk, does it wrong (or at least, completely differently), and not just for overloaded operators. Smalltalk has no precedence for mathematical operators at all, ever. So a + b * c will execute the + operation on a, passing in b, and then execute the * operation on that result, passing in c. Always. Thus, 2 + 3 * 5 = 25 in Smalltalk, even though it should equal 17. To get 17, you’d have to write the equation as 2 + (3 * 5). I always found that strange.

The canonical example for operator overloading is a class to represent Complex numbers. I will not break with tradition and will, in fact, steal borrow the one from the book. Here, then, is the definition of the Complex class

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)
    }

    override def toString() = {
        real + (if (imaginary < 0) "" else "+") + imaginary + "i"
    }
}

Notice that we have overridden both the + and * operators. They each take another instance of Complex as an argument, do the proper operations and return a new instance of Complex as their result. Just as you would expect. Now, to exercise these operators, we have this

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

val res = c1 + c2 * c3

printf("(%s) + (%s) * (%s) = %sn", c1, c2, c3, res)

val res1 = c1 + c2 * c3 + c1 * c2

printf("(%s) + (%s) * (%s) + (%s) * (%s) = %sn", c1, c2, c3, c1, c2, res1)

Lines 5 and 9 are the interesting parts. The result of running these statements looks like this

(1+2i) + (2-3i) * (3-1i) = 4-9i
(1+2i) + (2-3i) * (3-1i) + (1+2i) * (2-3i) = 12-8i

which is exactly what you’d expect.

Now, C++ people are probably saying, “But C++ has always done it right!” Indeed. But languages like Smalltalk and Scala are far more fun to work in.

Advertisements

7 thoughts on “Scala Gets Operator Overloading Right

  1. C++ doesn’t do it right; you can’t define new operators, only provide definitions for existing ones.

    (Also, I miss user-specified fixity, but that’s mostly just because I can never remember Scala’s builtin precedences :p)

  2. …I meant to have an IMO somewhere there, of course. Sorry if I came across as trying to pass off opinion as fact. ๐Ÿ˜ฆ

  3. It does make sense to override + and – on Date.

    Date + integer => Date
    Date – integer => Date
    Date – Date = integer

  4. DannyB: The usage you describe is a little ambiguous.. Is the integer a day, month, or year? I believe a more descriptive name would be in order. Fortunately Scala allows you to use any one parameter functions as operators, so you could easily have “+days”. “Date +days 100”

    Thanks,
    Braden

Comments are closed.