Overloading operators like "+" to work on new types, and maybe mixed types, requires each desired combination of operator and operand types to be implemented (as one can easily do in a language like C++, and presumably also in more modern languages). If there is any commonality between types as to how these operators are meant to work, then generics such as C++'s templates can also be used to reduce the number of implementations to those that are actually distinct.

I don't understand what problem the author is trying to solve here - maybe it's language specific? More related to dynamic typing and efficient dispatch?

In any case, operator overloading, while it can make for cute-to-read code, isn't really a good idea for code readability and maintenance.

> operator overloading, while it can make for cute-to-read code, isn't really a good idea for code readability and maintenance

so should we write `add_int_int(1,1)` and `add_int_double(1, 1.0)` and `add_double_double(1.0, 1.0)`?...

The expectation is that arithmetic operators are performing the corresponding arithmetic operations, so overloading for different arithmetic types (int, float, complex) doesn't produce any surprises or need to lookup operator definitions.

Applying arithmetic operators to non-arithmetic types starts to become a problem, even if some cases (set union/difference, string catenation) are natural...

The same goes for other operators... overloading '[]' indexing for maps as well as arrays seems natural, but would be highly confusing if you overloaded it for something unrelated to an index-like concept.

I think a problem here is that everyone has a different idea of when you hit the boundary between obvious/non-surprising and confusing, so you can't just say that overloading is OK as long as it is unsurprising.

Just because people have differing opinions (some well founded, some not!) doesn't mean that defining best practices doesn't make sense. Code readability and lack of surprise objectively are good things, and certainly important for large enterprise projects.

I think there are two alternate guidelines for use of operator overloading that would make sense.

1) Just don't overload operators at all for your own types! Leave that to the standard libraries and types that they define.

OR

2) Overload, but keep to the semantics of the operators as used by the language's built-in types (e.g. use arithmetic operators for arithmetic operations, comparison operators for ordering operations, etc). If your project was using a style guideline like this, then violations would be caught by code review (maybe automated by AI in the future?).

>I don't understand what problem the author is trying to solve here - maybe it's language specific? More related to dynamic typing and efficient dispatch?

The expression problem only arises in statically typed programming languages, it does not exist in dynamically typed programming languages.

Operating overloading has nothing to do with the problem and can not be used to resolve it. Operators are nothing more than a one-off syntax so we can use familiar childhood notation like a + b, etc... they are not particularly meaningful. The ability to overload operators or functions in general is also irrelevant since such overloads are resolved statically at compile time.

> The expression problem only arises in statically typed programming languages, it does not exist in dynamically typed programming languages.

Wadler's list of requirements for solving the expression problem include "with static checking that all cases are covered", so in one sense, yes, dynamic languages don't have this "problem". But in another sense, dynamic languages simply have no chance to solve the problem because they don't statically check anything.

It is true that it's much easier to do multiple dispatch and open-ended extension in dymamic languages. That's a nice benefit of them. But you do sacrifice all of the safety, code navigation, and performance of static types in order to get that.

The problem Wadler is trying to solve is "how can we have this sort of open-ended extension in both directions without giving up static safety?"

This isn't true. Julia manages to maintain good performance with multiple dispatch through lots of type inference in the compiler (and carefully written code in the language to preserve "type stability")

Yes, Julia is an interesting outlier in the design space. My understanding is that Julia gets the speed it has largely by JITting a lot of code for every instantation of a generic/dynamic function that it sees with all of the concrete incoming types.

That's an interesting point in the design space where you get the flexibility of dynamic types (and multiple dispatch!) and good runtime speed. But you pay for it with slower startup times, less predictable performance, and much higher memory usage. You are also married to a JIT. Julia really is designed to be run interactively in a REPL from source. The language isn't well-suited to compiling a standalone executable ahead of time. That makes perfect sense for its use cases, but would make it a challenge to adopt in other use cases.

(For example, I work on Dart which is mostly used for building mobile apps. That means we care deeply about executable size, startup speed, and the ability to compile ahead-of-time to native executables.)

> does not exist in dynamically typed programming languages

The "problem" exists for dynamically-typed languages as well. If you define a new class in Python, you still need to teach existing code how to operate on it (though you might be using inheritance to automatically derive some of those implementations, but inheritance obviously isn't limited to dynamic languages).

> Operating overloading has nothing to do with the problem

You've got T types (some new), and O operators (some new) and want to implement all operators for all types ... This is the exact definition of operator overloading.

There is no magic (other than inheritance or generics, if applicable) that will remove the need to individually implement all those O x T cases, and while that is obviously necessary, it is also all that you need to do.

If you are not talking about operator overloading - supporting the same operator for multiple different custom types, then what are you talking about ?!