With a Great Power comes Great Responsibility. I’m referring to the incredible power of defining custom operators as function names. I was convinced that this feature was introduced by C++, but a quick look on Wikipedia was enough to dispell this myth. Starting from Algol 68, programmers were enabled to redefine operators. Not all languages have this feature and even those who do vary in what the programmer can do.
The idea is cool, take for example math operators like +
and -
, they are widely and precisely understood, they are brief and concise. Programmers are usually happy identifying the default semantic for common language operators such as *
, /
, <
, >
and by language-specific domain, ==
, !=
and %
.
If you define your mathematical structure, then having the option to operate on them via mathematical operators is a great way to write clear and concise code.
What can possibly go wrong? Well, until now – at least in production code – I never found a +
operator redefined to perform subtraction. But I have found some cases where the programmer made up a custom semantic for operators applied to custom types.
The most innocent is the +
to concatenate strings. Note that although this may be quite natural to most programmers (exposed to high-level programming languages), it wouldn’t make sense for a mathematician. Just to point out the white elephant, +
is commutative, string concatenation is not.
But this slippery path looks so inviting – why not use /
to start parallel threads, or <<
to write stuff in a stream? This easily leads to write-only code where a restricted circle is able to type in the code faster and possibly understand it but leaves outsiders head scraping in front of mysterious gibberish.
Java saw that this was BAD™ and refused altogether the concept (this seems natural in Java’s mission to take blades, spikes and booms out of the programmer’s hand). If you want to add two vectors v1
and v2
, then call add(v1,v2)
and nobody gets hurt.
Time passes, Scala enters. Possibly to contest Java, Scala features an even greater power than C++, you can define any symbol you want as a custom operator. And since basically all types are boxed, custom operators may be applied to language types.
As for C++, this is not bad per se, but it is easy to be carried away and inventing every kind of funny operator, just to curse yourself a month later or being hunted by coworkers if you are not coding alone.
Which kind of custom operators have I found in (and removed from) my code? I add the types (something that you may not have the chance to find so easily in a source code where they are omitted everywhere possible)
-+(Byte) : Int
to convert bytes to their unsigned equivalents (replaced by an implicit class withtoUint
method).--->(JsValue) : Unit
to send data down the websocket.+?(T):Option[T]
to implement a sum that may or may not have a result.def ==(data: Array[Byte]) : Boolean
method of aGpioComm
class used to check if a packet is equal to the given byte sequence.
Lesson learned – despite being popularized by successful libraries (e.g. optics) refrain the temptation of defining custom operators. Really no one, but you may have a clue on what -@&£
means besides being random keystrokes. Not always your reader may have a modern IDE to look up the definition, and even if she has it, it is an extra time and effort needed to understand your code.
Alberto says:
As a mathematician, I am pretty at ease with non-commutative operations. Think about matrix multiplication, for instance. However it’s true that the symbol + is used more often in commutative contexts.
Anyway, if you introduce a new operator, be sure to give it a catchy name: https://devblogs.microsoft.com/cppblog/simplify-your-code-with-rocket-science-c20s-spaceship-operator/