Samstag, 28. September 2013

Operator Overloading: There is no "right" choice.

I've been looking into Xtend a lot lately, but I do not limit myself to one language. In fact, I've had an eye on all of the major competitors in the JVM language market. One thing that sparks many religious debates among the different communities is operator overloading. It's not just "whether" it should be allowed, but also "how". There are now four different approaches on the market and I will give you the pros and cons of each one without the usual fanboyism.

Java: No operator overloading


Without any operator overloading at all, you are stuck with what the core language provides. A very simple and annoying example of this is java.math.BigDecimal. Because there is no "+" operator for BigDecimal, you always have to write x.plus(y). Of course, this gets out of hand quickly. "(x + y) * z" becomes x.plus(y).multiply(z). On the other hand, when you see a "+" operator, you know exactly what it means and you can also deduct that the types to either side of the operator must be one of the primitive number types. But all in all, readability suffers greatly, especially with custom numeric types.

Xtend: Freely overloading predefined operators


In Xtend, you can freely overload any of the predefined operators. There is no restriction on the argument and return types. This is useful for writing internal DSLs. By adding operator overloading to the already awesome QueryDSL, the following Xtend code looks pretty close to an SQL where-clause:

The "==" operator does not compare two objects in memory, as it would do normally. It does not return "true" or "false". Instead it returns a "Predicate" which can then be transformed into an SQL query that is then executed against the database.

On the other hand, you can do all kinds of weird stuff with free overloading. When you see "x + y" in Xtend, it could mean literally anything. I could override "+" to make it add the length of two Strings instead of concatenating them. And sadly, many developers tend to go overboard like this. They come up with new meanings that have not much to do with the original use of the operator in question.

Scala: Do whatever you want


In Scala you can not only override existing operators, but you can define pretty much any operator you want. So if you want to emulate SQL syntax and you want the "<>" ("not equal") operator, you can just define it. In Xtend you would have to use the existing "!=" operator, which is probably just as understandable, but not exactly like in SQL. As a bonus, you can use infix notation for any binary function in Scala. So you can write "x IN (1, 2, 3)" like in SQL instead of "x.in(1, 2, 3)". It's a small difference but it quickly adds up as you can see in the following example taken from jOOQ:

On the other hand, things can get even more out of hand. Would you know what "x =#!> y" means? Any library author can come up with as many new operators as they want. And oh boy do they abuse this!

Ceylon: Operator Polymorphism


Ceylon is the new kid on the block and brings a fresh idea: Operators that are bound to interfaces. If you want your class to be usable with the "+" operator, you need to implement the "Summable" interface and its "plus" method. The "plus" method has a strict signature, so it will always take two things of the same type and return a result of the same type. This means you cannot create something like the QueryDSL and JOOQ examples I showed above. On the other hand, there is very little ambiguity as to what "x+y" means, no matter which code base you are just reading. This kind of discoverability is very important in big and long-lived code bases where the original authors my not even be around anymore.

Conclusion


In the end, I think only Java really falls short with its "no overloading at all" policy. All the other approaches have their merit. It all depends on the kind of project and team you have. The worst programmers are those who are intelligent enough to grasp the language concepts, but not smart enough to apply them only where appropriate. They seem to think that in order to master the language, they need to push it to its syntactical limits.

So if you have the right people writing your libraries, you'll be fine and you will get some awesome internal DSLs. If not, you should use a more restrictive language or you're in for a maintenance nightmare.