No, that’s just not true. It makes absolutely no difference for this kind of argument whether a language is single-dispatch (like Python) or multiple dispatch. In single-dispatch languages the only difference is that for a + b
, the first argument a
controls the implementation of +
, by default (in Python, via the __add__
dunder method). The expression a + b
is exactly as generic in Python as it is in Julia, and you cannot say anything about what the result of a + b
is unless you know the type of a
and b
. This does not lead to any kind of problems.
It means pretty little: “addition operator”, according to the docstring in Julia. There’s really not much of a contract implied there for what “addition operator” means. As discussed previously, you can’t even blindly assume that a + b
is commutative. I mean sure, you can have a strong convention that +
should be limited to mathematical summation of number-like objects (the details of which still entirely depend on the exact type of the objects). I just find that languages that use more “operator punning” (use +
for more than just numbers, with meanings different from just “mathematical summation”) end up being more pleasant to use. But that’s a matter of opinion. But the situation is exactly the same for Julia and Python. If a + b
being implemented for strings doesn’t cause problems in Python, it wouldn’t cause problems in Julia, either.
And +
is a trivial function. For less trivial functions, I would say it is impossible to come up with a generic function docstring that encompasses all the methods for all possible types or arguments in the entire ecosystem. It is completely normal (also in core Julia and the standard library) for method docstrings to augment the function docstring, specifying additional properties, or even deviating significantly from the generic docstrings. That’s why methods can have docstrings, not just functions. Of course, it’s a good guideline to write methods that don’t need docstrings (that just implement the behavior specified in the generic function docstring).
Arguably, Julia has to worry about this issue less than Python, though: In Python, type annotations are still completely optional. So, if you’re not using a type checker, and you’re writing a function f(a, b)
that contains a + b
somewhere inside of it, you cannot make any assumptions whatsoever on what a + b
evaluates to. In contrast, in Julia, you at least have the option to declare that as a method, function f(a::Float, b::Float)
, and then you do have a very clear contract about a + b
. Or, you could say f(a::Number, b::Number)
, and you’d still have a reasonably good contract (it’s a mathematical sum), but you wouldn’t be able to assume, e.g., commutativity. And, of course, you could have f(a::AbstractString, b::AbstractString)
, and that would be completely safe if +(a::AbstractString, b::AbstractString)
was defined, even if that method was completely different from the generic +
.
If we had a type system that was more powerful and included trait-like features, f(a::Number<:CommutativePlusTrait, b::Number:<CommutativePlusTrait)
, the entire problem would be pretty much solved.
Also, maybe we’re forgetting to state the obvious: Using *
for string concatenation is just as much a violation of the generic multiplication operator as +
would be a violation of the generic addition. And indeed, *
has a whole bunch of different method docstrings that deviate from the generic “Multiplication operator”. Going back to through the 10-year old discussions on GitHub about whether or not to change *
for string concatenation, that was exactly Stefan’s position at the time. I think that’s a perfectly respectable and consistent position to have. I don’t share it, but it’s a question of mentality, not of any objective truth.
So, basically, If the argument is, “Let’s be conservative about operator punning, and not overload *
or +
for strings, but let’s use ++
or ..
, or whatever”, I’d say “Sounds good!”
If the argument is, “We’re going to use *
for string concatenation, no reason”, then my response is “Fine. That might take a little getting used to, and I’m not sure it’s great that Julia is the only language using that operator. Maybe it’s better to borrow an operator from any of the n most popular languages. But it doesn’t really matter.”
If the argument is “We’re gong to use *
for string concatenation; footnote: because of non-commutative monoid” (the situation we’re in), then that’s going to raise an eyebrow. The healthy response would be the same as for “no reason”. Accept it, move on with your life (the answer to the original topic).
If the argument is “*
is the only appropriate operator for string concatenation, because of abstract string algebra in theoretical computer science”, then I’m starting to have serious problems. Because then we’re in the territory of “I need to understand abstract algebra concepts to work with Julia / Julia is only for PhDs”, which is not a good place to be in.
And if you then go further and say "because we have *
, abstract algebra demands that we also have /
(despite there not being a multiplicative inverse for strings), then I think we’ve totally lost the plot, and we end up with "Oh, then we must also have the same algebra with *
and /
for Path
objects, and then p"folder/sub" / p"sub"
ends up meaning the exact opposite of what it means in literally every other programming language that has Path
objects.