99
votes

One feature I miss in from functional languages is the idea that operators are just functions, so adding a custom operator is often as simple as adding a function. Many procedural languages allow operator overloads, so in some sense operators are still functions (this is very true in D where the operator is passed as a string in a template parameter).

It seems that where operator overloading is allowed, it is often trivial to add additional, custom operators. I found this blog post, which argues that custom operators don't work nicely with infix notation because of precedence rules, but the author gives several solutions to this problem.

I looked around and couldn't find any procedural languages that support custom operators in the language. There are hacks (such as macros in C++), but that's hardly the same as language support.

Since this feature is pretty trivial to implement, why isn't it more common?

I understand that it can lead to some ugly code, but that hasn't stopped language designers in the past from adding useful features that can be easily abused (macros, ternary operator, unsafe pointers).

Actual use cases:

  • Implement missing operators (e.g. Lua doesn't have bitwise operators)
  • Mimic D's ~ (array concatenation)
  • DSLs
  • Use | as Unix pipe-style syntax sugar (using coroutines/generators)

I'm also interested in languages that do allow custom operators, but I'm more interested in why it has been excluded. I thought about forking a scripting language to add user-defined operators, but stopped myself when I realized that I haven't seen it anywhere, so there's probably a good reason why language designers smarter than me haven't allowed it.

10
  • 4
    There is a Reddit discussion going on about this question.
    – Dynamic
    Dec 30, 2012 at 19:00
  • 2
    @dimatura: I don't know much about R, but it's custom operators still don't seem very flexible (e.g. no proper way do define fixity, scope, overloading etc.), which explains why they aren't used much. That's different in other languages, certainly in Haskell which uses custom infix operators a great lot. Another example of a procedural language with reasonably custom-infix support is Nimrod, and of course Perl also allows them. Dec 30, 2012 at 23:11
  • 4
    There's the other option, Lisp actually has no difference between operators and functions.
    – BeardedO
    Dec 31, 2012 at 13:22
  • 4
    No mention of Prolog yet, another language where operators are just syntactic sugar for functions (yes even math ones) and which allows you to define custom operators with custom precedence. Jan 1, 2013 at 23:00
  • 2
    @BeardedO, there are no infix operators in Lisp at all. Once you introduce them, you'll have to deal with all the issues with precedence and such.
    – SK-logic
    Jan 2, 2013 at 9:14

16 Answers 16

136
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.

8
  • 23
    On the two schools of thought, plus.google.com/110981030061712822816/posts/KaSKeg4vQtz also +1 for having the most direct (and likely most true) answer to the question of why language designers choose one vs. the other. I would also say based on your thesis you could extrapolate that fewer languages allow it because a smaller portion of developers are highly skilled than otherwise (the top percent of anything will always be a minority by definition) Dec 29, 2012 at 21:27
  • 13
    I think this answer is vague and only marginally related to the question. (I also happen to think that your characterisation is wrong since it contradicts my experience but that’s not important.) Dec 30, 2012 at 12:03
  • 1
    As @KonradRudolph said, this doesn't really answer the question. Take a language like Prolog which lets you do anything you want with operators including define their precedence when placed infix or prefix. I do not think the fact that you can write custom operators has anything to do with the skill level of the target audience, but rather because the goal of Prolog is to read as logically as possible. The inclusion of custom operators allows you to write programs that read very logically (after all a prolog program is just a bunch of logical statements). Jan 1, 2013 at 22:35
  • How did I miss that stevey rant? Oh man, bookmarked Jan 7, 2013 at 20:05
  • Into which philosophy would I fall if I think that programmers are most likely to write the best code if languages give them the tools to say when the compiler should make various inferences (e.g. auto-boxing arguments to a Format method) and when it should refuse (e.g. auto-boxing arguments to ReferenceEquals). The more ability language gives programmers to say when certain inferences would be inappropriate, the more safely it can offer convenient inferences when appropriate.
    – supercat
    Feb 24, 2014 at 23:40
85
votes

Given a choice between concatenating arrays with ~ or with "myArray.Concat(secondArray)", I would probably prefer the latter. Why? Because ~ is a completely meaningless character that only has its meaning - that of array concatenation - given in the specific project where it was written.

Basically, as you said, operators are no different from methods. But while methods can be given readable, comprehensible names that add to the understanding of the code flow, operators are opaque and situational.

This is why I also don't like PHP's . operator (string concatenation) or most of the operators in Haskell or OCaml, though in this case, some universally accepted standards are emerging for functional languages.

14
  • 28
    The same could be said of all operators. What makes them good, and can also make user-defined operators good (if they're defined judiciously), is this: Despite the character used for them being just a character, widespread use and possibly mnemonic properties make its meaning in source code immediately obvious for those who are familiar with it. So your answer as it stands isn't that convincing IMHO.
    – user7043
    Dec 29, 2012 at 18:38
  • 34
    As I mentioned at the end, some operators have universal recognizablity (such as the standard arithmentic symbols, bitwise shifts, AND/OR, and so forth), and so their terseness outweighs their opacity. But being able to define arbitrary operators seems to keep the worst of both worlds. Dec 29, 2012 at 18:44
  • 12
    So you're also in favor of prohibiting, for example, single-letter names at language level? More generally, not allowing anything in the language that seems to do more harm than good? That's a valid approach to language design, but not the only one, so I don't think you can't presuppose it.
    – user7043
    Dec 29, 2012 at 18:45
  • 9
    I don't agree that custom operators is "adding" a feature, it's removing a restriction. Operators are generally just functions, so instead of a static symbol table for operators, a dynamic, context-dependent one could be used instead. I imagine this is how operator overloads are handled, because + and << certainly aren't defined on Object (I get "no match for operator+ in ..." when doing that on a bare class in C++).
    – beatgammit
    Dec 29, 2012 at 20:09
  • 13
    What it takes a LONG time to figure out is that coding isn't usually about getting a computer to do something for you, it's about generating a document that both people and computers can read, with people being quite a bit more important than computers. This answer is exactly correct, If the next person (or you in 2 years) has to spend even 10 seconds trying to figure out what ~ means, you might as well have spent the 10 seconds typing in a method call instead.
    – Bill K
    Dec 30, 2012 at 20:06
72
votes

Since this feature is pretty trivial to implement, why isn't it more common?

Your premise is wrong. It’s not “pretty trivial to implement”. In fact, it brings a bag of problems.

Let’s have a look at the suggested “solutions” in the post:

  • No precedence. The author himself says “Not using precedence rules is simply not an option.”
  • Semantic aware parsing. Like the article says, this would require the compiler to have a lot of semantic knowledge. The article doesn’t actually offer a solution for this and let me tell you, this simply isn’t trivial. Compilers are designed as a trade-off between power and complexity. In particular, the author mentions a pre-parsing step to collect the relevant information, but pre-parsing is inefficient and compilers strive very hard to minimise parsing passes.
  • No custom infix operators. Well, that’s not a solution.
  • Hybrid solution. This solution carries many (but not all) of the disadvantages of semantic aware parsing. In particular, since the compiler has to treat unknown tokens as potentially representing custom operators, it often cannot produce meaningful error messages. It also may require the definition of said operator to proceed with parsing (to collect type information etc.), once again necessitating an additional parsing pass.

All in all, this is an expensive feature to implement, both in terms of parser complexity and in terms of performance, and it’s not clear that it would bring a lot of benefits. Sure, there are some benefits to the ability of defining new operators but even those are contentious (just look at the other answers arguing that having new operators isn’t a good thing).

9
  • 2
    Thanks for the voice of sanity. My experience suggests that people dismissing things as trivial are either experts or blissfully ignorant, and more often in the latter category :x Dec 30, 2012 at 17:37
  • 14
    every single one of these problems has been solved in languages that have existed for 20 years...
    – Philip JF
    Dec 31, 2012 at 4:38
  • 11
    And yet, it is exceptionally trivial to implement. Forget about all that dragon book parsing algorithms, it's a 21st century and it's a time to move on. Even extending a language syntax itself is easy, and adding operators of a given precedence is trivial. Take a look at, say, Haskell parser. It is much, much simpler than parsers for the "mainstream" bloated languages.
    – SK-logic
    Dec 31, 2012 at 9:39
  • 4
    @SK-logic Your blanket assertion doesn’t convince me. Yes, parsing has moved on. No, implementing arbitrary operator precedences depending on type is not “trivial”. Producing good error messages with limited context is not “trivial”. Producing efficient parsers with languages that require multiple passes to transform is not feasible. Jan 4, 2013 at 16:23
  • 6
    In Haskell, parsing is "easy" (compared to most languages). Precedence is context independent. The part that gives you hard error messages is related to typeclass and advanced type system features, not to user defined operators. It is overloading, not user definition, that is the hard problem.
    – Philip JF
    Jan 4, 2013 at 23:26
26
votes

Let's ignore the whole "operators get abused to harm readability" argument for the moment, and focus on the language design implications.

Infix operators have more issues than simple precedence rules (though to be blunt, the link you reference trivializes the impact of that design decision). One is conflict resolution: what happens when you define a.operator+(b) and b.operator+(a)? Preferring one over the other leads to breaking the expected commutative property of that operator. Throwing an error can lead to modules that would otherwise work become broken once together. What happens when you start throwing derived types into the mix?

The fact of the matter is that operators are not just functions. Functions either stand alone or are owned by their class, which provides a clear preference on which parameter (if any) owns the polymorphic dispatch.

And that ignores the various packaging and resolution problems that arise from operators. The reason languages designers (by and large) limit infix operator definition is because it creates a pile of problems for the language while providing debatable benefit.

And frankly, because they're not trivial to implement.

9
  • 1
    I believe this is the reason Java doesn't include them, but languages that do have them have clear rules for precedence. I think it's similar to matrix multiplication. It's not commutative, so you have to be aware when you're using matrices. I think the same applies when dealing with classes, but yes, I do agree that having a non-commutative + is evil. But is this really an argument against user-defined operators? It seems like an argument against operator overloading in general.
    – beatgammit
    Dec 29, 2012 at 20:27
  • 1
    @tjameson - Yes, languages do have clear rules for precedence, for math. The precedence rules for math operations will likely not really apply if the operators are being overloaded for things like boost::spirit. As soon as you allow user defined operators that gets worse since there is no good way to even well-define precedence for math. I have written a little about that myself in the context of a language that specifically looks to address issues with arbitrarily defined operators.
    – Telastyn
    Dec 29, 2012 at 20:37
  • 22
    I call bullshit, sir. There is life outside the OO-ghetto, and there functions do not necessarily belong to any object, therefore finding the right function is exactly the same as finding the right operator. Dec 30, 2012 at 17:35
  • 1
    @matthieuM. - Certainly. Ownership and dispatch rules are more uniform in those languages that do not support member functions. At at point though, the original question becomes 'why aren't non-OO languages more common?' which is a whole other ballgame.
    – Telastyn
    Dec 30, 2012 at 18:20
  • 11
    Have you looked at how Haskell does custom operators? They work exactly like normal functions, except they also have an associated precedence. (In fact, so can normal functions, so even there they don't really differ.) Basically, operators are infix by default and names are prefix, but that's the only difference. Dec 30, 2012 at 18:53
21
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.

6
  • I don't understand your Double.NaN example, this behavior is part of the floating point spec and is supported in every language I've used. Are you saying that custom operators aren't supported because it would confuse developers who don't know about the feature? That sounds like the same argument against using the ternary operator or your ?? example.
    – beatgammit
    Dec 29, 2012 at 20:20
  • @tjameson: You're right, actually, that was a bit tangential to the point I was trying to make, and not particularly well-written. I thought of the ?? example as I was writing that and prefer that example. Removing the double.NaN paragraph.
    – pdr
    Dec 29, 2012 at 20:24
  • 6
    I think your "every new developer was destined to be confused by this terminology" point is a bit lackluster, worrying about the learning curve of new developers to your code base is to me the same as worrying about the learning curve of new developers to a new version of .NET. I think more importantly is when the devs do learn it (new .NET or your code base), does it make sense and show value? If so, the learning curve is worth it, because each developer only needs to learn it once, though the code need be manhandled countless times, so if it improves the code it's a minor cost. Dec 29, 2012 at 21:08
  • 8
    @JimmyHoffa It's a recurring cost. Paid upfront for every new developer and also affecting existing developers because they have to support it. There is a documentation cost as well and an element of risk - it feels safe enough today, but 30 years down the line, everyone will have moved on, the language and the application are now "legacy", the documentation is a huge steaming pile and some poor sucker will be left tearing his hair at the brilliance of programmers who were too lazy to type ".concat()". Is the expected value is enough to offset the costs?
    – Sylverdrag
    Dec 30, 2012 at 6:25
  • 3
    Also, the argument "Every new developer was destined to be confused by this terminology. Do we not have enough problems learning a new domain?" could have been applied to OOP back in the 80s, structured programming before that, or IoC 10 years ago. It's time to stop using this fallacious argument. Mar 28, 2014 at 16:52
13
votes

I can think of a few reasons:

  • 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 customised 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 character is cheap, 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.
8
votes

Since this feature is pretty trivial to implement, why isn't it more common?

It isn't trivial to implement (unless trivially implemented). It also doesn't get you very much, even if implemented ideally: the readability gains from terseness are offset by the readability losses from unfamiliarity and opacity. In short, it's uncommon because it's not usually worth the developers' or the users' time.

That said, I can think of three languages that do it, and they do it in different ways:

  • Racket, a scheme, when it isn't being all S-expression-y allows and expects you to write a what amounts to parser for any syntax you want to extend (and provides useful hooks to make this tractable).
  • Haskell, a purely function programming language, allows defining any operator that consists solely of punctuation, and allows you to provide a fixity level (10 available) and an associativity. Ternary etc. operators can be created out of binary operators and higher order functions.
  • Agda, a dependently typed programming language, is extremely flexible with operators (paper here) allowing both if-then and if-then-else to be defined as operators in the same program, but its lexer, parser, and evaluator are all strongly coupled as a result.
1
  • 5
    You've said it "isn't trivial", and immediately listed three languages featuring some outrageously trivial implementations. So, it's quite trivial after all, isn't it?
    – SK-logic
    Jan 2, 2013 at 9:22
7
votes

One of the main reasons custom operators are discouraged is because then any operator can mean/can do anything.

For example cstream's much criticized left shift overload.

When a language allows operator overloads, there is generally an encouragement to keep the operator behavior similar to the base behavior to avoid confusion.

Also user defined operators makes the parsing much more difficult, especially when there is also custom preference rules.

5
  • 6
    I don't see how the parts about operator overloading apply to defining completely new operators.
    – user7043
    Dec 29, 2012 at 19:01
  • I've seen very good use of operator overloading (linear algebra libraries) and very bad as you mentioned. I don't think this is a good argument against custom operators. The parsing may be an issue, but it's pretty obvious when an operator is expected. After parsing, it's up to the code generator to look for operator meaning.
    – beatgammit
    Dec 29, 2012 at 19:49
  • 3
    And how is this different from method overloading? Dec 29, 2012 at 20:19
  • @JörgWMittag with method overloading there is (most of the time) a meaningful name attached. with a symbol it is harder to explain succinctly what will happen Dec 29, 2012 at 23:39
  • 1
    @ratchetfreak: Well. + add two things, - subtracts them, * multiplies them. My feeling is that noone forces programmer to make function/method add actually add anything and doNothing may launch nukes. And a.plus(b.minus(c.times(d)).times(e) is much less readable then a + (b - c * d) * e (added bonus - where in first sting is error in transcription). I don't see how first is more meaningful... Dec 30, 2012 at 19:15
5
votes

We do not use user-defined operators for the same reason we do not use user-defined words. No one would call their function "sworp". The only way to convey your thought to other person is to use shared language. And that means both words and signs (operators) must be known to the society for whom you are writing your code.

Therefore the operators you see in use in programming languages are the ones we've been taught in school (arithmetic) or the ones that have been established in programming community, like say boolean operators.

4
  • 1
    Add to that the ability to discover the meaning behind a function. In the case of "sworp" you can quite easily stick it into a search box and find it's documentation. Sticking an operator into a search engine is a whole different ball park. Our current search engines simply suck at finding operators, and I've wasted a lot of time trying to find the meanings in some cases.
    – Mark H
    Dec 31, 2012 at 1:22
  • 1
    How much "school" do you count? For example, I think ∈ for elem is a great idea and certainly an operator everybody should understand, but others seem to disagree. Dec 31, 2012 at 2:03
  • 1
    It's not the problem with the symbol. It's the problem with the keyboard. I for example use emacs haskell-mode with unicode beautifier enabled. And it automatically converts ascii operators to unicode symbols. But i would not be able to type it if there would not be an ascii equivalent. Dec 31, 2012 at 8:32
  • 2
    Natural languages are built of the "user-defined words" only and nothing else. And some languages actually do encourage custom word forming.
    – SK-logic
    Jan 2, 2013 at 9:24
4
votes

You're fighting against two things here:

  1. Why do operators exist in languages in the first place?
  2. What is the virtue of operators over functions/methods?

In most languages, operators are not really implemented as simple functions. They might have some function scaffolding, but the compiler/runtime is explicitly aware of their semantic meaning and how to translate them efficiently in to machine code. This is much more true even as compared to built-in functions (which is why most implementations also don't include all the function call overhead in their implementation). Most operators are higher level abstractions on primitive instructions found in CPUs (which is partly why most operators are arithmetic, boolean, or bitwise). You could model them as "special" functions (call them "primitives" or "builtins" or "native" or whatever), but to do that generically requires a very robust set of semantics for defining such special functions. The alternative is to have built-in operators that semantically look like user defined operators, but which otherwise invoke special paths in the compiler. That runs afoul of the answer to the second question...

Aside from the machine translation issue I mentioned above, at a syntactical level operators aren't really different from functions. They're distinguishing characteristics tend to be that they are terse and symbolic, which hints at a significant additional characteristic they must have to be useful: they must have broadly understood meaning/semantics to developers. Short symbols don't convey much meaning unless it is short hand for a set of semantics that are already understood. That makes user defined operators inherently unhelpful, as by their very nature they aren't so broadly understood. They make as much sense as one or two letter function names.

C++'s operator overloads provides fertile ground for examining this. Most operator overload "abuse" comes in the form of overloads that break some of the semantic contract that is broadly understood (a classic example is an overload of operator+ such that a + b != b + a, or where + modifies either of its operands).

If you look at Smalltalk, which does allow operator overloading and user defined operators, you can see how a language might go about doing it, and how useful it would be. In Smalltalk, operators are merely methods with different syntactical properties (namely, they are encoded as infix binary). The language uses "primitive methods" for special accelerated operators and methods. You find that few if any user defined operators are created, and when they are, they tend not to get used as much as the author probably intended for them to be used. Even the equivalent of an operator overload is rare, because it is mostly a net loss to define a new function as an operator instead of a method, as the latter allows for an expression of the semantics of the function.

4
votes

As to languages that do support such overloading: Scala does, actually in a much cleaner and better way can C++. Most characters can be used in function names, so you can define operators like !+*=++ , if you like. There is built-in support for infix (for all functions taking one argument). I think you can define the associativity of such functions, too. You can't however, define the precedence (only with ugly tricks, see here).

4
votes

One thing that hasn't been mentioned yet is the case of Smalltalk, where everything (including operators) is a message send. "Operators" like +, | and so on are actually unary methods.

All methods can be overridden, so a + b means integer addition if a and b are both integers, and means vector addition if they're both OrderedCollections.

There are no precedence rules, since these are just method calls. This has an important implication for standard mathematical notation: 3 + 4 * 5 means (3 + 4) * 5, not 3 + (4 * 5).

(This is a major stumbling block to Smalltalk newbies. Breaking mathematics rules removes a special case, so that all code evaluation proceeds uniformly left to right, making the language that much simpler.)

2
votes

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" and it would do the right thing on all of our supported platforms). 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.

0
votes

Operator overloads are a bad idea for the same reason that method overloads are a bad idea: the same symbol on the screen would have different meanings depending upon what is around it. This makes it more difficult for casual reading.

Since readability is a critical aspect of maintainability, you should always avoid overloading (except in some very special cases). It is far better for each symbol (whether operator or alphanumeric identifier) to have a unique meaning that stands on its own.

To illustrate: when reading unfamiliar code, if you encounter a new alphanum identifier that you don't know, at least you have the advantage that you know you don't know it. You can then go look it up. If, however, you see a common identifier or operator that you know the meaning of, you are far less likely to notice that it actually has been overloaded to have a completely different meaning. To know what operators have been overloaded (in a code base that made widespread use of overloading), you would need a working knowledge of the complete code, even if you only want to read a small part of it. This would make it hard to bring new developers up to speed on that code, and impossible to bring people in for a small job. This may be good for programmer job security, but if you are responsible for the success of the code base, you should avoid this practice at all costs.

Because operators are small in size, overloading operators would allow more dense code, but making code dense is not a real benefit. A line with twice the logic takes twice as long to read. The compiler does not care. The only issue is human readability. Since making code compact does not enhance readability, there is no real benefit to compactness. Go ahead and take the space, and give unique operations a unique identifier, and your code will be more successful in the long run.

1
  • "the same symbol on the screen would have different meanings depending upon what is around it" - it is the case already for many operators in most languages.
    – rkj
    Jul 27, 2013 at 2:01
-1
votes

Technical difficulties to handle precedence and complex parsing set aside, I think there are a few aspects of what a programming language is that has to be considered.

Operators are typically short logical constructs that are well defined and documented in the core language (compare, assign..). They are also typically hard to understand without documentation (compare a^b with xor(a,b) for instance). There are a rather limited number of operators that actually might make sense in normal programming (>,<, =,+ etc..).

My idea is that it's better to stick to a set of well defined operators in a language - then allow operator overloading of those operators (given a soft recommendation that the operators should do the same thing, but with a custom data type).

Your use cases of ~ and | would actually be possible with simple operator overloading (C#,C++, etc.). DSL is a valid usage area, but probably one of the only valid areas (from my point of view). I, however, think that there are better tools to create new languages within. Executing a true DSL language within another language is not that difficult by using any of those compiler-compilers tools. Same goes for the "extend LUA argument". A language is most likely defined primarily to solve problems in a specific way, not to be a foundation for sub-languages (exceptions exists).

-1
votes

Another factor for it is that it's not always straight forward to define an operation with the available operators. I mean, yes, for any kind of number, the '*' operator can make sense, and are usually implemented in the language or in existing modules. But in the case of the typical complex classes that you need to define (things like ShipingAddress, WindowManager, ObjectDimensions, PlayerCharacter, etc) that behaviour is not clear... What does mean to add or subtract a number to an Address? Multiply two Addresses?

Sure, you can define that adding a string to an ShippingAddress class means a custom operation like "replace line 1 in address"(instead of "setLine1" function) and adding a number is "replace zip code" (instead of "setZipCode"), but then the code is not very readable and confusing. We typically think that operator are used in basic types/classes, as their behaviour is intuitive, clear and consistent (once you are familiar with the language, at least). Think in types like Integer, String, ComplexNumbers, etc.

So, even if defining operators can be very useful in some specific cases, their real-world implementation is quite limited, as the 99% of the cases where that will be a clear win are already implemented in the basic language package.

Not the answer you're looking for? Browse other questions tagged or ask your own question.