On 09/09/2015 01:32 AM, Jonathan M Davis wrote:
On Tuesday, 8 September 2015 at 20:55:35 UTC, Timon Gehr wrote:
On 09/08/2015 06:49 PM, Jonathan M Davis wrote:
Sure, it _could_ be implemented that way, but the only reason I
see to do that is if we're specifically looking to support defining
overloaded operators outside of the types that they apply to. I can't
think of anything else that would be affected by it.
...

The compiler does not match the specification. I see no reason to
change the specification here, but it would be easy.

I don't know where you get this idea.

Lowering transforms D code to other D code.

See http://dlang.org/operatoroverloading.html :

We have that e.g. "-e" gets rewritten to "e.opUnary!("-")()".
"e.opUnary!("-")()" is D code. To specify the semantics implemented in DMD, the rewrite should be __traits(getMember,e,"opUnary")!("-")() (assuming that the parser is fixed so it can actually parse this, which would be useful in its own right.)

Regardless, I honestly think that it would be a very bad technical
decision to support defining overloaded operators outside of the type
itself - _especially_ when you take into account operators that have
defaults generated by the compiler (e.g. opEquals), since that would
allow a third party to change what your code does by adding their own
overloaded operator.

Well, how? "Overloaded operators" are just specially named functions
that support an additional call syntax. There are no strange issues
with modularity that somehow only apply to overloaded operators. ...

There would be no way to disambiguate overloaded operators if an
operator were overloaded in multiple modules. ...
...

Yes, there would be. Just use aliases. (Also, overloaded operators are not actually that likely to cause conflicts, because they are best written to match a suitably constrained set of types, just like other functions.)

UFCS is already enough of a problem on its own. It has some benefits,
but it doesn't work when conflicts come into play, forcing you to just
call the function normally,

No, it does not force you to do that.

and its overload rules are such that it
doesn't actually prevent hijacking in all cases (e.g. your code could
suddenly change behavior, because you used UFCS with a type that then
had a function with the same name and parameters added to it).

I think that's universally understood, but I don't see how this relates to the issue at hand.

And
overloaded operators are closely tied to what a type does, whereas
functions are far more diverse.  So, there's a lot more to be gained with
UFCS than with declaring overloaded operators separately from a type.

UFCS and this ad-hoc terminology do not describe separate things.

So that you can declare multiple
overloads of it for when you import different modules? That would just
be plain confusing and incredibly error-prone.

(Straw man.)

And if the problem is
that you're dealing with someone else's type, then just declare a
function to do what you want and be done with it.

I agree with this part, overloaded operators are just such functions.

And do you really want
people to be able to overload stray operators for stuff like strings and
then write code that uses + and - and / or whatever on them? That would
be ludicrous. It's one thing for people to do that with their own types.
It's quite another to do that to built-in types or to someone else's
types.

This is not distinct from being able to declare functions operating on built-in types or someone else's types that have other unsuitable names. Also, in case you missed it, I also briefly promoted the idea that built-in types should define the operator overloading functions in order to make treatment of operators uniform (as far as the specification is concerned, the compiler could just rewrite the special members to whatever internal representation it uses now for the built-in types). In particular, "+", "-" and "/" can't be overloaded externally on strings in such a setting.

What do you want next?

(That's a nice way of introducing a straw man.)

To be able to declare constructors for someone else's type?

That's not even close to being similar.

That kind of stuff

:o)

AFAICT, they were just clustered together randomly in order to artificially boost the argument.

is part of the type, and
allowing 3rd parties to declare it as well just makes it that much
harder to figure out what's going on. At least when using UFCS, it's the
exception that the function is on the type, so the overload rules don't
usually shoot you in the foot (though they can), and you know to look
elsewhere for the function just like you would with a function that was
called normally. It's the complete opposite with operators.
...

I don't see where you get this idea. It should be noted that operator overloading isn't even that common and basically all legitimate usages are obvious. (They are those cases where a operator is the most natural name for a function.)

We have overloaded operators so that someone who is writing a
user-defined type can make it act like a built-in type where
appropriate. Allowing 3rd party code to declare overloaded operators for
a type doesn't help with that at all.
...

On 09/09/2015 01:32 AM, Jonathan M Davis wrote: (moved from above)
I really don't see any reason why it would even make sense to declare
operators separately from a type.

One reason is that single dispatch can be awkward. A textbook example would be: you have a set of special matrix types (dense, sparse, diagonal, triangular, column-first, row-first, etc.), and now want to implement multiplication efficiently for all pairs of types via static dispatch. Why should I have to jump through hoops just to be able to use suitable function names that are associated with the wanted call syntax?

Another reason is that it is annoying that overloaded operators choose the calling convention for you, asymmetrically only for the first argument.

It certainly shouldn't be hard to produce more of those examples. Non-orthogonal features tend to cause subtle (and less subtle) pain points.


It would be better already if UFCS only worked for overloaded operators if the type and the operator where defined in the same module, but I really don't see the point of adding arbitrary limitations to the language. There might be legitimate use cases.

There is _nothing_ in the spec which supports UFCS having anything to do
with overloaded operators

On 09/09/2015 01:32 AM, Jonathan M Davis wrote: (moved from the beginning of the post)
The spec says _nothing_ about UFCS
applying to overloaded operators or that UFCS would apply to lowered
code in any way shape or form.
...


Please stop palindrome-posting. (This is a general tendency in your posts, I wouldn't have brought it up otherwise: they (approximately) start with topic A, go to topic B, then C, return to B and then finish with A. Together with the missing emphasis on conciseness, it can get a little bit tiring at times.)

If you read http://dlang.org/function.html#pseudo-member carefully, nowhere is it explicitly mentioned that it works with built-in types or structs, or with classes named "NoUFCS". Would you also claim that the intention of the specification to support UFCS for those types is questionable? Your argument seems to apply equally well to those cases.

and nothing to support overloading operators
separately from a type. And I recall Walter Bright stating that it was
on purpose that you can only overload operators on a type. So, even if
the spec _could_ be interpreted to mean that you should be able to
declare overloaded operators separately from the type that they apply
to, that's _not_ its intention, and you're going to have to convince
Walter if you want anything else.
...

I might do that eventually, but there are more important things to consider at this point.

Reply via email to