Why aren’t user-defined operators more common?

Developers who can handle it still have to work with developers who can't.

This Q&A is part of a weekly series of posts highlighting common questions encountered by technophiles and answered by users at Stack Exchange, a free, community-powered network of 100+ Q&A sites.

Where have all the user-defined operators gone?tjameson can't "find any procedural languages that support custom operators in the language," he writes. "There are hacks (such as macros in C++), but that's hardly the same as language support." Is there any good reason we can't write our own operators? See the original question here.

Are you experienced?

Karl Bielefeldt answers (94 votes): There are two diametrically opposed schools of thought in programming language design. One is that programmers write better code with fewer restrictions, and the other is that they write better code with more restrictions. In my opinion, the reality is that good experienced programmers flourish with fewer restrictions, but that restrictions can benefit the code quality of beginners. User-defined operators can make for very elegant code in experienced hands, and utterly awful code by a beginner. So whether your language includes them or not depends on your language designer's school of thought. Related: "How are operators saved in memory?"

Rollin' with your homies

pdr answers (13 votes): I think you'd be surprised how often operator overloads are implemented in some form. But they're not commonly used in a lot of communities. Why use ~ to concatenate to an array? Why not use << like Ruby does? Because the programmers you work with are probably not Ruby programmers. Or D programmers. So what do they do when they come across your code? They have to go and look up what the symbol means. I used to work with a very good C# developer who also had a taste for functional languages. Out of the blue, he started introducing monads to C# by way of extension methods and using standard monad terminology. No one could dispute that some of his code was terser and even more readable once you knew what it meant, but it did mean that everyone had to learn monad terminology before the code made sense. Fair enough, you think? It was only a small team. Personally, I disagree. Every new developer was destined to be confused by this terminology. Do we not have enough problems learning a new domain? On the other hand, I will happily use the ?? operator in C# because I expect other C# developers to know what it is, but I wouldn't overload it into a language that didn't support it by default.

Let me count the ways

They aren't trivial to implement—allowing arbitrary custom operators can make your compiler much more complex, especially if you allow user-defined precedence, fixity, and arity rules. If simplicity is a virtue, then operator overloading is taking you away from good language design.

They get abused—mostly by coders who think it is "cool" to redefine operators and start redefining them for all sorts of custom classes. Before long, your code is littered with a load of customized symbols that nobody else can read or understand because the operators don't follow the conventional well-understood rules. I don't buy the "DSL" argument, unless your DSL happens to be a subset of mathematics :-)

They hurt readability and maintainability—if operators are regularly overridden, it can become hard to spot when this facility is being used, and coders are forced to continually ask themselves what an operator is doing. It is much better to give meaningful function names. Typing a few extra characters is cheap and long term maintenance problems are expensive.

They can break implicit performance expectations.—For example, I'd normally expect lookup of an element in an array to be O(1). But with operator overloading, someobject[i] could easily be an O(n) operation depending on the implementation of the indexing operator.

In reality, there are very few cases where operator overloading has justifiable uses compared to just using regular functions. A legitimate example might be designing a complex number class for use by mathematicians, who understand the well-understood ways that mathematical operators are defined for complex numbers. But this really isn't a very common case. Some interesting cases to consider:

Lisps—in general don't distinguish at all between operators and functions, - + is just a regular function. You can define functions however you like (typically there is a way of defining them in separate namespaces to avoid conflict with the built-in +), including operators. But there is a cultural tendency to use meaningful function names, so this doesn't get abused much. Also, in Lisp, prefix notation tends to get used exclusively, so there is less value in the "syntactical sugar" that operator overloads provide.

Java—disallows operator overloading. This is occasionally annoying (for stuff like the Complex number case) but on average it's probably the right design decision for Java which is intended as a simple, general-purpose OOP langauge. Java code is actually quite easy for low/medium-skilled developers to maintain as a result of this simplicity.

C++—has very sophisticated operator overloading. Sometimes this gets abused (cout << "Hello World!" anyone?) but the approach makes sense given C++'s positioning as a complex language that enables high level programming while still allowing you to get very close to the metal for performance, so you can e.g. write a Complex number class that behaves exactly as you want without compromising performance. It is understood that it is your own responsibility if you shoot yourself in the foot.

I read a book about C++ when I was a teenager and thought "wow! I can define my own operators!" In the 15 or so years since I read that book, I've only really wanted to define my own operator or overload an existing one about twice. I'm sure it would be useful if you have to do the same operation on your custom data types all the time, but only if it's something all your co-workers will know about, like if it's in a shared library.

I've overloaded operators, but generally their only for custom objects that contain some set of numbers - for instance, an array of N length that needs to be added to a separate array of M length, with attributes to determine if we need to average of rate the smaller array down up to the length of the larger array before performing the operation.

I've overloaded operators, but generally their only for custom objects that contain some set of numbers - for instance, an array of N length that needs to be added to a separate array of M length, with attributes to determine if we need to average of rate the smaller array down up to the length of the larger array before performing the operation.

Sort of an edge case.

This is actually the case where I think operating overloading is the most harmful. Because there is no clear commonly accepted way to handle adding two arrays of different length this is something that's better done with a function. If I see a plus sign in code I always want to know exactly how that works without having to look iat the implementation. The array example violates that rule.

The real question is why most languages distinguish between operators and functions/procedures/methods. They're essentially the same thing and should work the same way. Thus, when functions can be "overloaded", for example when implementations of an interface can specify the definitions of functions, one should be able to overload operators too.

Look to Haskell for the logical way to treat "operators": they're defined just as regular functions. The "built-in" operators like == and + are just part of the standard library. For example, + and - are part of the class (analogous to Java interface) Num, and any implementation of Num provides an implementation of those functions. A programmer can make functions from some type T to numbers into numbers themselves, where addition and subtraction are performed point-wise:

Well, Python's magic methods let you do something similar to operator overloading... but you can't just make up arbitrary symbols and expect them to work.

Which you can't in any other language either (although there are languages that allow a much larger range of symbols in function names) - at least none that I know of.

Personally operator overloading is nice for mathematical concepts like adding complex numbers, vectors, etc. For anything else they generally lead to more confusion than they help. Don't think I've seen any proponent of operator overloading use something more exotic than maybe a Currency class for their demonstration (and even that is already slightly problematic depending on the definition).

That said having to use .plus() and co when adding BigIntegers together in Java is horrible so if you won't allow operator overloading at least make sure to make exceptions for the obvious cases. I doubt many people had much problems with the lack of operator overloading in Java if the BigInt/BigDecimal and some vector (mathematical concept not the actual existing class) class had the stuff defined..

Operator overloading is one of those features that sounds great, but really has to get used carefully. Does adding two vectors a+b concatenate them or add them element by element or do an outer product style addition? How hard is it for the reader to find out?

Even in mathematical code, you have to be careful. Many algorithms that work in the real domain will be wildly unstable if you just slide in complex operations.

If you are working in a particular problem domain where it makes sense for the team, go ahead and use overloading. If you are operating on polynomial rings or exploiting vector operations, it can bring new programmers up to speed a lot more quickly than a package library, particularly if they are domain experts. On the other hand, make sure you do the investment in training and documentation or you'll wind up paying more than you planned.

I had some genetic algorithms (implemented in C++) that would start with an array of 105 objects which constituted a generation. A new generation could be produced by simply incrementing the current one:

NextGen = ThisGen++;

And within the generation, the objects overloaded the '*' operator. That way,

child = mom * dad;

See? They're multiplying! Get it?

I thought these overloads were pretty clear and made the code very simple and intuitive. If the argument against them is that you can't tell HOW these operations are implemented, isn't that usually true of all operators?

Well, Python's magic methods let you do something similar to operator overloading... but you can't just make up arbitrary symbols and expect them to work.

Which you can't in any other language either (although there are languages that allow a much larger range of symbols in function names) - at least none that I know of.

Personally operator overloading is nice for mathematical concepts like adding complex numbers, vectors, etc. For anything else they generally lead to more confusion than they help. Don't think I've seen any proponent of operator overloading use something more exotic than maybe a Currency class for their demonstration (and even that is already slightly problematic depending on the definition).

That said having to use .plus() and co when adding BigIntegers together in Java is horrible so if you won't allow operator overloading at least make sure to make exceptions for the obvious cases. I doubt many people had much problems with the lack of operator overloading in Java if the BigInt/BigDecimal and some vector (mathematical concept not the actual existing class) class had the stuff defined..

I find that operator overloading provides terse syntax for operations which in most cases deserve an infix operator. I can think of a couple good examples from the Nimrod programming language's standard library (http://nimrod-code.org/): Overloading of / to provide platform specific joining of filepaths (http://nimrod-code.org/os.html#128) and the =~ operator which allows for easy regex matching (http://nimrod-code.org/re.html#117 ; I believe perl has something similar).

I had some genetic algorithms (implemented in C++) that would start with an array of 105 objects which constituted a generation. A new generation could be produced by simply incrementing the current one:

NextGen = ThisGen++;

And within the generation, the objects overloaded the '*' operator. That way,

child = mom * dad;

See? They're multiplying! Get it?

I thought these overloads were pretty clear and made the code very simple and intuitive. If the argument against them is that you can't tell HOW these operations are implemented, isn't that usually true of all operators?

The argument is that you don't know what they _do_ without looking at their implementation. What's involved in the mom * dad operation? Does it overwrite all the mothers properties with the dads and then create a child using that? does it randomly select from each? There's no way of knowing.

In your case I'd say this is much less harmful than the array addition overload up above because it's unlikely that someone looking at this code will just assume that they know what the multiplication operator does. However there's another problem that you have. What's the performance characteristic of that operation. All of the default operators are quick and constant time. So when someone sees mom * dad; they might think that it is fast and safe to call anywhere when in fact it might do any number of slow things including hitting the network or writing to disk.

I agree with solomonoff. Fundamentally, operators really are just functions and should have the same treatment by the compiler as any other function (the runtime still optimizes operators on primative types, but that's an implementation detail).

That using symbol characters to define functions rather than words causes readability issues is irrelevant, and in my opinion stems more from the incorrect idea that operators and functions are different things. The same could be said of using single character names, or of words that are vague or misleading.

In terms of violating programmer assumptions regarding performance: what had this to do with operator overloading? I can just as easily violate an unspoken performance contract without operator overloading. For example, if I implemented the IList interface in .net but made random access O(N) instead of O(1). If operators have such implied contracts, then they are once again no different from other non operator functions that have similar contracts.

No language can possibly prevent unreadable code from being written, or shield new programmers on a project from having to learn something new in order to get up to speed. Or prevent programmers from making bad decisions. The fact of the matter is that treating operators differently from functions only leads to inconsistency in your syntax. And, in my opinion, the fact that there is one VERY legitimate use case for operator overloading on math related types, like vectors, matrices, quaternions, complex numbers, and BigInteger/BigDecimal types is enough to warrant their existence in any general purpose programming language. Even if that's the only legitimate use case!

After all, YOU may not work with vectors or matrices every day, and so you think it's not a common use case, but to someone who DOES work with those types, it is a very common case indeed, and operator overloading dramatically improves the readability of their code.

I had some genetic algorithms (implemented in C++) that would start with an array of 105 objects which constituted a generation. A new generation could be produced by simply incrementing the current one:

NextGen = ThisGen++;

And within the generation, the objects overloaded the '*' operator. That way,

child = mom * dad;

See? They're multiplying! Get it?

I thought these overloads were pretty clear and made the code very simple and intuitive. If the argument against them is that you can't tell HOW these operations are implemented, isn't that usually true of all operators?

The argument is that you don't know what they _do_ without looking at their implementation. What's involved in the mom * dad operation? Does it overwrite all the mothers properties with the dads and then create a child using that? does it randomly select from each? There's no way of knowing.

In your case I'd say this is much less harmful than the array addition overload up above because it's unlikely that someone looking at this code will just assume that they know what the multiplication operator does. However there's another problem that you have. What's the performance characteristic of that operation. All of the default operators are quick and constant time. So when someone sees mom * dad; they might think that it is fast and safe to call anywhere when in fact it might do any number of slow things including hitting the network or writing to disk.

I had some genetic algorithms (implemented in C++) that would start with an array of 105 objects which constituted a generation. A new generation could be produced by simply incrementing the current one:

NextGen = ThisGen++;

Probably a typo, but that sets NextGen to the current value of ThisGen, then increments ThisGen after the assignment. Perhaps you meant:

NextGen = ++ThisGen;

?

Of course, that ends up with NextGen and ThisGen having the same value... And if the above don't hold because you somehow overloaded post-increment to return the incremented value, then you're proving the point about the dangers of operator overloading and developers not maintaining language conventions/norms.

I agree with solomonoff. Fundamentally, operators really are just functions and should have the same treatment by the compiler as any other function (the runtime still optimizes operators on primative types, but that's an implementation detail).

That using symbol characters to define functions rather than words causes readability issues is irrelevant, and in my opinion stems more from the incorrect idea that operators and functions are different things. The same could be said of using single character names, or of words that are vague or misleading.

In terms of violating programmer assumptions regarding performance: what had this to do with operator overloading? I can just as easily violate an unspoken performance contract without operator overloading. For example, if I implemented the IList interface in .net but made random access O(N) instead of O(1). If operators have such implied contracts, then they are once again no different from other non operator functions that have similar contracts.

No language can possibly prevent unreadable code from being written, or shield new programmers on a project from having to learn something new in order to get up to speed. Or prevent programmers from making bad decisions. The fact of the matter is that treating operators differently from functions only leads to inconsistency in your syntax. And, in my opinion, the fact that there is one VERY legitimate use case for operator overloading on math related types, like vectors, matrices, quaternions, complex numbers, and BigInteger/BigDecimal types is enough to warrant their existence in any general purpose programming language. Even if that's the only legitimate use case!

After all, YOU may not work with vectors or matrices every day, and so you think it's not a common use case, but to someone who DOES work with those types, it is a very common case indeed, and operator overloading dramatically improves the readability of their code.

You're right, operators and functions are just different syntax for the same underlying concept. But the reason that they have different syntax is that they come with a set of assumptions when most people look at them.

I come from a programming background where we had about 2000 developers working on a single product. In that kind of environment it's very important for people to be able to glance at code and understand what's going on and where they need to dig deeper. Assumptions and conventions are very important in this kind of environment.

EDIT -- Hit submit too soon. Again I agree with you there are lots of legitimate use cases for operator overloading. Complex numbers is a good example. There's only one reasonable definition for adding two complex numbers so that's a great place to use overloading to make code more readable at a glance. Where I think they cause the most problem is with mathematical operations that are undefined where you make up a definition that 100% of people might not agree with.

Mathematical operators naturally convey mathematical concepts. When you see a + or a *, you automatically expect it to be transitive, symmetrical, associative, have a neutral element, etc...These properties can be attained only if you manipulate mathematical algebraic objects. They have little sense when manipulating other concepts.

Other operators like ->, >>, ++, etc have no mathematical equivalence and can have different meanings in different languages. Their usage convey no more information than a method name, and can likely be more confusing.

Add a plethora of other problems (see mikera's answer in orignal story), and you understand that using operators instead of method names make precious little sense in most cases. One could as well use method names 'A', 'b', 'x', 'Z', etc...

If you really really want to use operators, consider using the R statistical language that manipulates directly mathematical concepts.

I find that operator overloading provides terse syntax for operations which in most cases deserve an infix operator. I can think of a couple good examples from the Nimrod programming language's standard library (http://nimrod-code.org/): Overloading of / to provide platform specific joining of filepaths (http://nimrod-code.org/os.html#128) and the =~ operator which allows for easy regex matching (http://nimrod-code.org/re.html#117 ; I believe perl has something similar).

Interesting example and the path concatenation one is probably the best non-math example for operator overloading that I've come across so far. I agree looks nicer than the function call and should be unsurprising (although overloading such a general class as string for one specific problem is less nice, the ubiquity of the problem makes it a sensible tradeoff though)

I had some genetic algorithms (implemented in C++) that would start with an array of 105 objects which constituted a generation. A new generation could be produced by simply incrementing the current one:

NextGen = ThisGen++;

Probably a typo, but that sets NextGen to the current value of ThisGen, then increments ThisGen after the assignment. Perhaps you meant:

NextGen = ++ThisGen;

Yeah obvious typo, but just to be clear here: Both versions (post or prefix) are undefined behavior in C++ which I assume is what we're talking about here. Ah c++..

I think its rarely beneficial and more of a gadget than useful in large projects. It's a bit like advanced implicit constructors: they may do a lot of (possible unintended) stuff that is hard to spot.

As c++ is specially mention: If you overload && or || you loose the short circuit mechanism. The overloaded operator becomes a normal function and both arguments will always be evaluated. Whatever you do, do never, ever, overload &&/|| in c++.

The problem with operator overloading is that its never entirely clear when its happening in real code.

x = a * b

With rampant operator overloading, I have to know the types of a and b before I can even begin to suspect that operator overloading might be going on.

Except that in any language I've seen that has integer and floating point arithmetic, * is overloaded for both, and thus you need to know the types of a and b to know what's really happening. But much of the time you don't care what's really happening: all you care about is the abstraction that * provides. Polymorphism allows you to write algorithms that care about the abstraction, not the underlying types: for example, to sum the elements of a list as in a fold operation, all you care about is that there's some zero object and some way of adding two objects, and thus you don't care what numerical type you're operating on. Thus it's not only unnecessary, but also bad, to require a usage of a function to indicate the concrete type of object it's operating on.

Quote:

Now, if you could define new operators with different symbols from the current ones, there would be less confusion.

x = a ● b

wow - now there is an operator never before seen - time to look up what it means

Other operators like ->, >>, ++, etc have no mathematical equivalence and can have different meanings in different languages. Their usage convey no more information than a method name, and can likely be more confusing.

Add a plethora of other problems (see mikera's answer in orignal story), and you understand that using operators instead of method names make precious little sense in most cases. One could as well use method names 'A', 'b', 'x', 'Z', etc...

If you really really want to use operators, consider using the R statistical language that manipulates directly mathematical concepts.

Using operators in a non-mathematical context is only confusing because of a prejudice from C-style languages where operators (instead of equality and perhaps a couple others) are essentially only restricted to certain domains. There's nothing inherent to math or non-math domains that makes symbols vs. names more or less useful. Math uses symbols because of their terseness, and such terseness can be useful in other domains. For example, you may have an infix operator "?" that takes a boolean expression as its first argument and a pair as its second, and yields the first or second element of the pair if the boolean expression evaluates to true or false, respectively: this is essentially the ternary operator.

As c++ is specially mention: If you overload && or || you loose the short circuit mechanism. The overloaded operator becomes a normal function and both arguments will always be evaluated. Whatever you do, do never, ever, overload &&/|| in c++.

The fundamental problem is C++'s strictness, meaning its insistence on evaluating function arguments before calling the function, not in operator overloading.

Functions or methods which use verb phrases are readable. Overloaded punctuation symbols aren't, and make the code look like a game of Nethack, or even APL. So good, literate code tells you what it does as you read it. The only time user-defined operators seem to make sense is when they parallel a mathematical operation, such as overloading arithmetic operations for, say, a matrix or something the language doesn't have built in. (But then you run into problems. Is "x" an operator for cross product? Or do you overload Unicode symbols? What is the best operator for dot and cross product? What happens when you try to multiply the wrong size matrices? Does your operator throw an exception, and does any other operator in your language throw exceptions?)

I look at operator overloading from the perspective of weighing the time saved over the product lifetime because of increased clarity using operator overloading vs. extra time used because of maintenance mistakes + the time spent to eventually rewrite code with operator overloading because maintenance was too hard.

On that basis, I consider operator overloading to be a net negative for a language. It's just too elegant a feature for many of us to resist when we're "living the code", yet it usually ends in tears for those who have to come after us (or for ourselves after we've forgotten everything about the project and then have to go back 5 years later.)

OO *can* be used responsibly, but the odds that it *will* be used responsibility are low enough that it's better (from a global programmer productivity perspective) not to have it at all.

Are you using the language to do work, or are you goofing around trying to rewrite the language for your own convenience or amusement? Are you sure the point isn't just to be impressive? Because, paradoxically, that's only going to be annoying.

The custom behavior will stick out like the proverbial sore thumb. Developers fluent in the language will already know their own recipes for the problem likely to be solved by custom operators, and such recipes are likely to be fairly familiar to any other competent developer. By chucking all that and changing how operators work, you abandon all that experience, and you incur costs in time as each new developer encounters its use and unravels its meaning, wasting their time – which is more valuable than a few cycles.

I would especially abhor using it to make one language behave like another. Again, developers who have internalized the language at hand will know the patterns best suited to that language. Shoehorning patterns from other languages into a project imposes a cognitive penalty on developers who know your project's native language, as they have to somehow know when a given expression is behaving like the one language or the other. Confusion, errors, and bugs are bound to ensue.

Granted, an adept developer can handle all of this easily enough. But why should they have to?

Just use the language as it comes. Stick to self-descriptive methods and functions when you need extended behavior. Take advantage of the features of the language that developers familiar with it will understand at no further thought. When that includes operator overloading, fine, but ask yourself whether it's helpful, or whether it's merely clever. If it's only clever, and your documentation is going to take more time than just writing a method or function that can explain itself, a named procedure of some kind is far preferable.

Well, Python's magic methods let you do something similar to operator overloading... but you can't just make up arbitrary symbols and expect them to work.

Which you can't in any other language either (although there are languages that allow a much larger range of symbols in function names) - at least none that I know of.

Prolog allows defining your own operators, with whatever characters you like. You can specify both precedence and associativity, too. Prolog uses "=<" for less than or equal, so that the arrows ("<=" and "=>") are available to be overloaded.

Bjarne Stroustrup's original C++ book unintentionally shows why allowing programmer-defined operators is generally a really bad idea. When talking about stream output and how "<<" was chosen, he says:

"The operators < and > were tried, but the meanings 'less than' and 'greater than' were so firmly implanted in people's minds that the new I/O statements were for all practical purposes unreadable... The operators << and >> do not appear to cause that kind of problem."

In other words, overloading a common operator to something other than it's normal meaning was just too confusing. It was necessary to pick the "bit shift" operator, which very few people used. In fact, if you asked most programmers what the meaning of '<<' is, they'd probably be hard pressed to remember that it is originally a bit shift operation.

Functions or methods which use verb phrases are readable. Overloaded punctuation symbols aren't, and make the code look like a game of Nethack, or even APL. So good, literate code tells you what it does as you read it. The only time user-defined operators seem to make sense is when they parallel a mathematical operation, such as overloading arithmetic operations for, say, a matrix or something the language doesn't have built in. (But then you run into problems. Is "x" an operator for cross product? Or do you overload Unicode symbols? What is the best operator for dot and cross product? What happens when you try to multiply the wrong size matrices? Does your operator throw an exception, and does any other operator in your language throw exceptions?)

Operator overloading's a power tool, and you don't just throw away your powerdrill because somebody could stick it up his nose.

Abusing it is no good, of course, but there's simple answer to "How do I know what it does?" - you read the fabulous manual! Just like you have to read it to know that floating point arithmetic is not associative and distributive and that computing x=x++ + ++x; can invoke nasal demons in C++, but has specification defined semantics in JS.

Cleverly written embedded DSL with operator overloading can save you a lot of typing and reading time.

Does operator overloading provide any advantage other than syntactic sugar over plain ole functions?

My problem with operator overloading is that nothing in code is "sacred" anymore. In languages without OO, I can take operators for granted. In languages, and code where OO is prevelant, it is always a potential landmine.

I don't really have anything against it if used judiciously, but I just don't see the advantages being worth the potential issues, unless we are talking about a domain such as mathematics where those operators already have fundamental, well defined meanings.

In C++, operator overloading is frequently put to good use when overloading operators '()', '++' (prefix) and '++' (suffix), '*' (dereference) and '->' (member dereference). Iterator and function objects make extensive use of such operator overloading.

Things get murky when binary operators are overloaded or, more to the point, when an operator overload involves an intermediate value, which one may consider to be a subtotal involved in a larger expression and otherwise can't be saved as a return value. On top of that, things get even more complicated when different operators are intermixed in the same expression and operator precedence has to be considered. Coercion of operands can be an issue when subtypes are used for types for which operators have been overloaded.

Operator overloading has its benefits but, more often than not, it's much more complicated to be useful than the manner in which operator overloading if presented. There's a lot of theory involved in the innocuous looking operators. To equip a programming language with support for typical use of operator overloading is more difficult than one would imagine. Interpreted languages are likely more amenable to provide operator overloading than compiled languages. With compiled languages, you would hope that you could get away with simply implementing some localized functions but you end up having to implement a(n expression) language interpreter in the background, at least, if your intention is to produce a system of operators that are notationally useful. In the end, there's very little you can do to get around the associativity inherent in each language, which leads to an inability to realize the same efficiency you would get with an expression parser or even just with straightforward (and often notationally awkward) function calls.

I think Python is a good example of how operator overloading is used. Python provides the ability to overload nearly every operator ('and' and 'or' can't be overloaded because they short-circuit). However, the strong convention is that you only overload an operator to emulate the same behaviour (not necessarily performance) as a standard library class. Note that this is *only* a convention (as is pretty much everything in Python) but conventions have very strong weight in the Python community and code which does not follow the conventions is unlikely to gain much of a following.

The biggest advantage of operator overloading is that it allows a class to be a drop-in replacement for a standard library class, syntax and all. This reduces the burden on the user, rather than increasing it.

For example, one of the most commonly overloaded operators is indexing. This is implemented by overriding the __getitem__ method, giving a class the ability to index like x[a]. There is a very strong convention to only override __getitem__ if you are performing a lookup. In fact, there was recently a discussion on python-dev to decide if the new Enum class should implement __getitem__, and if so what the behaviour should be.

A less-commonly implemented, but slightly more abused operator is +, implemented by overriding __add__, __radd__ (reversed order of operands if the first operand can't perform the add) and __iadd__ (in-place addition). + has several meanings in the Python standard library. The most common is numeric addition, which has all the normal mathematical properties of addition. Any numeric class is expected to implement + appropriately if it makes sense.

Python also uses + for adding sequences (including strings). In this case, addition is expected to implement concatenation. Many user-defined sequence classes will implement + to mean concatenation.

By the time operator overloading becomes useful (vectors, complex numbers, possibly array or string concatenation, etc.) it'd make more sense for it to be a built-in language feature than something everyone has to implement in their own half-compatible ways.

I've always found operator overloads in C++ to be a convenient shortcut for a single-developer team, but which causes all sorts of confusion in the long term simply because the method calls are being "hidden" in a way that isn't easy for tools like doxygen to pick apart, and people need to understand the idioms in order to make use of them properly.

Sometimes it's a lot harder to make sense of than you'd expect, even. Once upon a time, in a large cross-platform C++ project I decided it'd be a good idea to normalize the way that paths were built by creating a FilePath object (similar to Java's 'File' object), that would have operator/ used to concatenate another path part on it (so you could do something like File::getHomeDir()/"foo"/"bar"). Everyone who saw it would essentially say, "What the hell? String division? ... Oh, that's cute, but I don't trust it to do the right thing."

Similarly, there's a lot of cases in graphics programming or other areas where vector/matrix math happens a lot where it's tempting to do things like Matrix*Matrix, Vector*Vector (dot), Vector%Vector (cross), Matrix*Vector (matrix transform), Matrix^Vector (special-case matrix transform ignoring the homogenous coordinate - useful for surface normals), and so on, but while it saves a bit of parsing time for the person who wrote the vector math library, it only ends up confusing the issue more for others. It just isn't worth it.

By the time operator overloading becomes useful (vectors, complex numbers, possibly array or string concatenation, etc.) it'd make more sense for it to be a built-in language feature than something everyone has to implement in their own half-compatible ways.

So every language will have built in support for 2D vectors, points, rectangles, 3D vectors, points, matrices, 4D matrices as well as rational numbers, complex numbers, quaternions, octonions, tensors, ....

I had some genetic algorithms (implemented in C++) that would start with an array of 105 objects which constituted a generation. A new generation could be produced by simply incrementing the current one:

NextGen = ThisGen++;

And within the generation, the objects overloaded the '*' operator. That way,

child = mom * dad;

See? They're multiplying! Get it?

I thought these overloads were pretty clear and made the code very simple and intuitive. If the argument against them is that you can't tell HOW these operations are implemented, isn't that usually true of all operators?

The argument is that you don't know what they _do_ without looking at their implementation. What's involved in the mom * dad operation? Does it overwrite all the mothers properties with the dads and then create a child using that? does it randomly select from each? There's no way of knowing.

In your case I'd say this is much less harmful than the array addition overload up above because it's unlikely that someone looking at this code will just assume that they know what the multiplication operator does. However there's another problem that you have. What's the performance characteristic of that operation. All of the default operators are quick and constant time. So when someone sees mom * dad; they might think that it is fast and safe to call anywhere when in fact it might do any number of slow things including hitting the network or writing to disk.

It seems sort of harmless, but if

child = mom * dad;

Then I'd also expect the following to be true:

mom * dad == mom * dad;

And also for

child = mom;child *= dad;

child == mom* dad;

And right at child *= dad is where the whole thing breaks down for the OP. "See what I did there, that's clever." is what I like to refer to as technical debt. Clever is great until the next guy isn't as clever or assumes like the follow on poster here, that just because one operator is implemented that all of them are implemented. Throw that same child *= dad in a long function where the declarations are not readily visible on the screen and it is reasonable to assume that if the compiler doesn't bark at child *= dad then the types are probably ok. Programmers will simply accept that int * int is going to work and will not validate it before using *= /= += or any other number of like operators. In essence, implementing operator overloads is like creating your own base type. As any framework author will tell you, when you implement something so foundational in your code, you better be thorough in its implementation before that code see another user's hands.

Clever in this case is a maintenance nightmare for the guy that sees this 5 years from now. Keep operator overloading in academics and in the few places where it makes perfect sense. For the rest of us, avoid it.

The problem with operator overloading is that its never entirely clear when its happening in real code.

x = a * b

With rampant operator overloading, I have to know the types of a and b before I can even begin to suspect that operator overloading might be going on.

I don't disagree with your overall point; in the case where knowledge of operator overloading is significant to using the operator for some reason. And in those cases, maybe a function is the better approach. But in some cases it seems this level of invisibility works as more a feature than a bug.

For example, setting up a framework to facilitate compiling and deploying applications across multiple platforms (the example I'm thinking of is a C++ game engine). The approach of using custom classes to abstract native data types and then overloading the full range of applicable operators works quite well to smooth-over quirky differences in how different operating systems on different hardware implement native type operations - particularly dealing with endian issues. Platform-specific code components will implement a full range of operators for each internal data type to coerce any platform-specific native deviations into compliance with the project's adopted expectations.

Once such a framework is set up, the programmer neither needs nor wants to think about the overloading - they just have to know that operations performed on internal data types will work exactly the same regardless environment being complied for.

To me, there needs to be a really good reason to use the overloading approach though - it's not something to do just for the sake of doing it. If there isn't a compelling argument for overloading being the very best solution my instinct is to accomplish the task a different way. But every calculation is different.

Another comment speculated that overloading to multiply vectors was a bad approach because it's unclear exactly *how* the operation is being carried out, which can be important ... on the flip side to that, if it is desirable for vector multiplication to be carried out in a specific standardized way across a project - overloading * and mandating it's use for vectors in whatever passes for a team's style-guide is a great way to avoid inconsistent internal vector approaches ... and provides the sort of notational convenience that encourages developers to quickly adopt use.

In short, overloading a way cool thing ... unless it isn't. And vice versa.

It's kind of hard to have a discussion about this when people don't even understand the difference between operator overloading and custom-defined operators. And if your complaint about operator overloading is that it makes it possible to break the implicit contract that comes with the operator, then, well, this has nothing to do with operators at all; you already have the same problem when overloading functions. Now to get back on topic, nobody seriously disputes that a >> b >> c >> d is more readable than a.followedBy(b.followedBy(c.followedBy(d))), do they?