Re: Operator overloading through UFCS doesn't work
On Tuesday, May 31, 2016 14:11:58 ixid via Digitalmars-d-learn wrote: > On Sunday, 29 May 2016 at 07:18:10 UTC, Jonathan M Davis wrote: > > And the fact that allowing free functions to overload operators > > via UFCS sends us into that territory just highlights the fact > > that they're a horrible idea. > > > > - Jonathan M Davis > > Do you have any examples of UFCS doing bad things? Most people > seem to very much like it yet you argue against any change that > would benefit UFCS. > > You seem to prefer: > > read(to(easier(much(i over i.much.easier.to.read The primary benefit of UFCS is that you can write generic code that will work with both member functions and free functions, allowing you to have a free function that does something and a member function that does that same thing more efficiently for that specific type (a prime example of this would be a function like find where a linear search make sense in most cases but wouldn't for certain data structures - e.g. a sorted, binary tree). So, the "universal" aspect of UFCS is important for generic code, whereas it would be completely unnecessary if the code weren't generic. All of the other benefits of UFCS are highly subjective and have to do with what a particular person thinks is easier or harder to read rather than actual, technical benefits (though obviously writing code in a way that is easier to read for those working on it is obviously valuable). Personally, I've dealt with functional languages enough that I've never felt that UFCS was much of an improvement syntactically. But we have it, and anyone is free to use it or not as they see fit. Regardless, what I'm arguing against here is altering operator overloading so that it works with free functions via UFCS instead of requiring that it be part of the type. It's a terrible idea IMHO to allow random code to add an overloaded operator to a type rather having it actually be part of the type's design, and in addition to that, it doesn't play at all nicely with symbol conflicts, because you're using an operator rather than a function, meaning that not only do you have no way to specify which version of the overloaded operator code should use, but it would completely defeat the purpose of using an overloaded operator in the first place even if you could. But fortunately, Walter agrees with me (or at least did, the last time the subject came up in the newsgroup), so I don't think that I have to worry about overloaded operators be definable via free functions. - Jonathan M Davis
Re: Operator overloading through UFCS doesn't work
On Sunday, 29 May 2016 at 07:18:10 UTC, Jonathan M Davis wrote: And the fact that allowing free functions to overload operators via UFCS sends us into that territory just highlights the fact that they're a horrible idea. - Jonathan M Davis Do you have any examples of UFCS doing bad things? Most people seem to very much like it yet you argue against any change that would benefit UFCS. You seem to prefer: read(to(easier(much(i over i.much.easier.to.read
Re: Operator overloading through UFCS doesn't work
Here's one more vote for extending UFCS to operator overloading. Elie wrote that it's "a restriction that seems pointless and arbitrary"... which summarizes my own thoughts rather well, too. There are certainly concerning scenarios that can arise from making this change, but the correct way to approach this problem is not to tell the programmer "I won't let you use that tool, because if you mishandle it then you might find yourself in a nasty mess." That's what Java does - it treats the programmer like an idiot - and that's why it's so universally despised. It has consistently been my impression that this is very much not the sort of philosophy D follows. Anyway, D already provides the programmer with a wealth of tools which, if mishandled, can place them in a nasty mess. So I think this is a poor rationale for withholding from the programmer one more.
Re: Operator overloading through UFCS doesn't work
On Sunday, 29 May 2016 at 07:18:10 UTC, Jonathan M Davis wrote: On Friday, May 27, 2016 09:08:20 Marc Schütz via Digitalmars-d-learn wrote: On Thursday, 26 May 2016 at 06:23:17 UTC, Jonathan M Davis wrote: > The difference is that it's impossible to do > 10.opBinary!"+"(15), so if you're forced to do > foo.opBinary!"+"(bar) to get around a symbol conflict, it > won't > work with built-in types. Well, that begs the question: Why don't built-in types define `opBinary`? That's just another arbitrary irregularity, isn't it. It was never intended that any op* function be called by anyone except where the compiler lowers code to use them. They're for declaring overloaded operators on user-defined types so that those types can be used with those operators. If you're calling opBinary in your own code, you're doing it wrong. And it would be downright silly to then add opBinary to the built-in types. If I were to design my own language from scratch, that's actually how I would do it. All operators, even for built-in types, would just be syntax sugar for the method calls. The goal should be to minimize the difference between built-in and user-defined types as much as possible. Turtles all the way down... They don't need operator overloading. They already have the operators. Operators are supposed to be used as operators, not functions, and if there's any need to use them as functions, then there's something seriously wrong. And the fact that allowing free functions to overload operators via UFCS sends us into that territory just highlights the fact that they're a horrible idea. I'd say the fact that it doesn't work, and can't currently work for the reasons you described, points to an inconsistency in the language's design. It means that we have two largely overlapping concepts (builtin types and user defined types), where most language features work the same for both, but some don't. That's not the end of the world, of course, but still...
Re: Operator overloading through UFCS doesn't work
On Friday, May 27, 2016 09:08:20 Marc Schütz via Digitalmars-d-learn wrote: > On Thursday, 26 May 2016 at 06:23:17 UTC, Jonathan M Davis wrote: > > The difference is that it's impossible to do > > 10.opBinary!"+"(15), so if you're forced to do > > foo.opBinary!"+"(bar) to get around a symbol conflict, it won't > > work with built-in types. > > Well, that begs the question: Why don't built-in types define > `opBinary`? That's just another arbitrary irregularity, isn't it. It was never intended that any op* function be called by anyone except where the compiler lowers code to use them. They're for declaring overloaded operators on user-defined types so that those types can be used with those operators. If you're calling opBinary in your own code, you're doing it wrong. And it would be downright silly to then add opBinary to the built-in types. They don't need operator overloading. They already have the operators. Operators are supposed to be used as operators, not functions, and if there's any need to use them as functions, then there's something seriously wrong. And the fact that allowing free functions to overload operators via UFCS sends us into that territory just highlights the fact that they're a horrible idea. - Jonathan M Davis
Re: Operator overloading through UFCS doesn't work
On Thursday, 26 May 2016 at 06:23:17 UTC, Jonathan M Davis wrote: The difference is that it's impossible to do 10.opBinary!"+"(15), so if you're forced to do foo.opBinary!"+"(bar) to get around a symbol conflict, it won't work with built-in types. Well, that begs the question: Why don't built-in types define `opBinary`? That's just another arbitrary irregularity, isn't it.
Re: Operator overloading through UFCS doesn't work
On 25.05.2016 01:19, Elie Morisse wrote: On Saturday, 13 October 2012 at 22:58:56 UTC, Timon Gehr wrote: Afaik free-function operator overloads (but not in the context of UFCS) were considered and turned down because D did not want to get amidst discussions about adding Koenig lookup. UFCS does not do Koenig lookup. I don't get it, aren't the current symbol lookup rules enough to make free function operator overloads useful? To me it looks like they are. ... Yup. It could be argued that it is essentially a compiler bug. Sorry for digging up this thread, just getting irritated by a restriction that seems pointless and arbitrary. ... It is, but it has a few vocal proponents. Overloaded operators would suffer from the same potential abuses other methods are subjected to if UFCS was enabled, nothing more as far as I can see. You are perfectly right of course. It's painful for no benefit. (For example, there's no way to overload e.g. '+=' for classes in a way that reassigns the reference.)
Re: Operator overloading through UFCS doesn't work
On Thursday, May 26, 2016 16:24:37 Elie Morisse via Digitalmars-d-learn wrote: > On Thursday, 26 May 2016 at 06:23:17 UTC, Jonathan M Davis wrote: > > The difference is that it's impossible to do > > 10.opBinary!"+"(15), so if you're forced to do > > foo.opBinary!"+"(bar) to get around a symbol conflict, it won't > > work with built-in types. > > Obviously operator overloading should be limited to class and > struct types, so that's not really relevant. It's completely relevent, because generic code is frequently going to operate on a variety of types - including both built-in types and user-defined types. If code is going to use +, then + must work. opBinary!"+" is simply not going to cut it. With UFCS, if there's a potential symbol conflict, then you can work around it - even in generic code. But with an overloaded operator, the operator must work as an operator, or generic code simply won't work. > > UFCS does not have that problem, because you're dealing with > > free functions and can choose to not use UFCS and provide the > > full import path or to alias the function, which you can't do > > with operators - particularly built-in operators. > > I still don't understand what you're getting at, unfortunately. You don't _have_ to use UFCS. You can call the function as a normal free function with the full import path. You can also alias the function to a different name and use that with UFCS or import the conflicting function with an alias so that it doesn't conflict. The overloaded operator, on the other hand, needs to work as on overloaded operator, and we can't just call an overloaded operator with its full import path or alias it when importing it, because it's an overloaded operator and not just a free function. We have ways to distinguish conflicting types with free functions, and we don't with operators, because operators were designed to be part of the type and not free functions. And syntactically, it really doesn't work to provide a way to distinguish between conflicting versions of an overloaded operator, because then you're no longer just using the operator and might as well just be using a function. But all I'm really doing here is saying the same thing multiple times in slightly different ways, so if you don't get it, I don't know how to explain it. > Thanks for taking the time to explain, although I still fail to > see a good justification for disabling UFCS for operators. I will > look for more discussions on the topic and if still no opposing > argument seems valid I might push the issue forward. You're going to need more than a good reason why it shouldn't be allowed. You're going to need a good reason why it should be, and I really don't think that you're going to have one good enough to get you anywhere with Walter. I'm fairly certain that there's a thread or two floating around somewhere in the main newsgroup where he's responded to the issue before though. - Jonathan M Davis
Re: Operator overloading through UFCS doesn't work
On Thursday, 26 May 2016 at 06:23:17 UTC, Jonathan M Davis wrote: The difference is that it's impossible to do 10.opBinary!"+"(15), so if you're forced to do foo.opBinary!"+"(bar) to get around a symbol conflict, it won't work with built-in types. Obviously operator overloading should be limited to class and struct types, so that's not really relevant. UFCS does not have that problem, because you're dealing with free functions and can choose to not use UFCS and provide the full import path or to alias the function, which you can't do with operators - particularly built-in operators. I still don't understand what you're getting at, unfortunately. D was designed to be much cleaner with operator overloading than C++ is. It restricts what the definitions of the operators are so that you don't have to define as many of them to get the basic operations (e.g. opCmp for most of the comparison operators or op!"++" for both pre-increment and post-increment) and so that they aren't easily overloaded to do stuff that does not correspond to what that operator does for built-in types. D doesn't even use + for string concatenation, because Walter thought that that was operator abuse. Allowing arbitrary code to add overloaded operators to an existing type is not at all in line with that philosophy. Regardless, there really isn't much point in arguing this. If you want things to change, you're going to need to convince Walter, which I very much doubt is going to happen. - Jonathan M Davis Thanks for taking the time to explain, although I still fail to see a good justification for disabling UFCS for operators. I will look for more discussions on the topic and if still no opposing argument seems valid I might push the issue forward.
Re: Operator overloading through UFCS doesn't work
On Wednesday, May 25, 2016 23:31:18 Elie Morisse via Digitalmars-d-learn wrote: > On Wednesday, 25 May 2016 at 21:50:06 UTC, Jonathan M Davis wrote: > > It's not an overloaded operator anymore at that point, and that > > definitely fails to work for generic code, since not all > > operators are overloaded operators. Free functions don't have > > that problem. > > Sorry to reiterate the previous post but is that really the case? > >void FuncTemplate(...)(...) { > X.FreeFunc(Y); >} > >import ModuleA; // contains FreeFunc >import ModuleB; // contains a conflicting FreeFunc overload > >FuncTemplate!()(); // fails > > Where is the difference with writing generic code with operators > (overloaded or not)? The difference is that it's impossible to do 10.opBinary!"+"(15), so if you're forced to do foo.opBinary!"+"(bar) to get around a symbol conflict, it won't work with built-in types. UFCS does not have that problem, because you're dealing with free functions and can choose to not use UFCS and provide the full import path or to alias the function, which you can't do with operators - particularly built-in operators. D was designed to be much cleaner with operator overloading than C++ is. It restricts what the definitions of the operators are so that you don't have to define as many of them to get the basic operations (e.g. opCmp for most of the comparison operators or op!"++" for both pre-increment and post-increment) and so that they aren't easily overloaded to do stuff that does not correspond to what that operator does for built-in types. D doesn't even use + for string concatenation, because Walter thought that that was operator abuse. Allowing arbitrary code to add overloaded operators to an existing type is not at all in line with that philosophy. Regardless, there really isn't much point in arguing this. If you want things to change, you're going to need to convince Walter, which I very much doubt is going to happen. - Jonathan M Davis
Re: Operator overloading through UFCS doesn't work
On Wednesday, 25 May 2016 at 21:50:06 UTC, Jonathan M Davis wrote: It's not an overloaded operator anymore at that point, and that definitely fails to work for generic code, since not all operators are overloaded operators. Free functions don't have that problem. Sorry to reiterate the previous post but is that really the case? void FuncTemplate(...)(...) { X.FreeFunc(Y); } import ModuleA; // contains FreeFunc import ModuleB; // contains a conflicting FreeFunc overload FuncTemplate!()(); // fails Where is the difference with writing generic code with operators (overloaded or not)?
Re: Operator overloading through UFCS doesn't work
On Wednesday, May 25, 2016 15:46:23 Elie Morisse via Digitalmars-d-learn wrote: > On Tuesday, 24 May 2016 at 23:43:46 UTC, Jonathan M Davis wrote: > > On Tuesday, May 24, 2016 23:19:32 Elie Morisse via > > > > Digitalmars-d-learn wrote: > >> On Saturday, 13 October 2012 at 22:58:56 UTC, Timon Gehr wrote: > >> > Afaik free-function operator overloads (but not in the > >> > context of UFCS) were considered and turned down because D > >> > did not want to get amidst discussions about adding Koenig > >> > lookup. UFCS does not do Koenig lookup. > >> > >> I don't get it, aren't the current symbol lookup rules enough > >> to make free function operator overloads useful? To me it > >> looks like they are. > >> > >> Sorry for digging up this thread, just getting irritated by a > >> restriction that seems pointless and arbitrary. > >> > >> Overloaded operators would suffer from the same potential > >> abuses other methods are subjected to if UFCS was enabled, > >> nothing more as far as I can see. > > > > If UFCS doesn't work, because there are two free functions with > > the same name which take the same arguments, then you can > > differentiate between them by not using UFCS and using their > > full import paths, or you can alias them so that they don't > > have the same name. Neither of those would be possible with > > operator overloading. If overloaded operators were allowed as > > free functions, then if there were ever a symbol conflict, > > you'd be screwed. > > > > - Jonathan M Davis > >X.FreeFunc(Y); // multiple matches error >ModuleA.FreeFunc(X, Y); // ok > >X * Y; // multiple matches error >ModuleA.opBinary!'*'(X, Y); // ok > > Is there much of a difference between the two? It's not an overloaded operator anymore at that point, and that definitely fails to work for generic code, since not all operators are overloaded operators. Free functions don't have that problem. - Jonathan M Davis
Re: Operator overloading through UFCS doesn't work
On Tuesday, 24 May 2016 at 23:43:46 UTC, Jonathan M Davis wrote: On Tuesday, May 24, 2016 23:19:32 Elie Morisse via Digitalmars-d-learn wrote: On Saturday, 13 October 2012 at 22:58:56 UTC, Timon Gehr wrote: > Afaik free-function operator overloads (but not in the > context of UFCS) were considered and turned down because D > did not want to get amidst discussions about adding Koenig > lookup. UFCS does not do Koenig lookup. I don't get it, aren't the current symbol lookup rules enough to make free function operator overloads useful? To me it looks like they are. Sorry for digging up this thread, just getting irritated by a restriction that seems pointless and arbitrary. Overloaded operators would suffer from the same potential abuses other methods are subjected to if UFCS was enabled, nothing more as far as I can see. If UFCS doesn't work, because there are two free functions with the same name which take the same arguments, then you can differentiate between them by not using UFCS and using their full import paths, or you can alias them so that they don't have the same name. Neither of those would be possible with operator overloading. If overloaded operators were allowed as free functions, then if there were ever a symbol conflict, you'd be screwed. - Jonathan M Davis X.FreeFunc(Y); // multiple matches error ModuleA.FreeFunc(X, Y); // ok X * Y; // multiple matches error ModuleA.opBinary!'*'(X, Y); // ok Is there much of a difference between the two?
Re: Operator overloading through UFCS doesn't work
On Tuesday, May 24, 2016 23:19:32 Elie Morisse via Digitalmars-d-learn wrote: > On Saturday, 13 October 2012 at 22:58:56 UTC, Timon Gehr wrote: > > Afaik free-function operator overloads (but not in the context > > of UFCS) were considered and turned down because D did not want > > to get amidst discussions about adding Koenig lookup. UFCS does > > not do Koenig lookup. > > I don't get it, aren't the current symbol lookup rules enough to > make free function operator overloads useful? To me it looks like > they are. > > Sorry for digging up this thread, just getting irritated by a > restriction that seems pointless and arbitrary. > > Overloaded operators would suffer from the same potential abuses > other methods are subjected to if UFCS was enabled, nothing more > as far as I can see. If UFCS doesn't work, because there are two free functions with the same name which take the same arguments, then you can differentiate between them by not using UFCS and using their full import paths, or you can alias them so that they don't have the same name. Neither of those would be possible with operator overloading. If overloaded operators were allowed as free functions, then if there were ever a symbol conflict, you'd be screwed. - Jonathan M Davis
Re: Operator overloading through UFCS doesn't work
On Saturday, 13 October 2012 at 22:58:56 UTC, Timon Gehr wrote: Afaik free-function operator overloads (but not in the context of UFCS) were considered and turned down because D did not want to get amidst discussions about adding Koenig lookup. UFCS does not do Koenig lookup. I don't get it, aren't the current symbol lookup rules enough to make free function operator overloads useful? To me it looks like they are. Sorry for digging up this thread, just getting irritated by a restriction that seems pointless and arbitrary. Overloaded operators would suffer from the same potential abuses other methods are subjected to if UFCS was enabled, nothing more as far as I can see.
Re: Operator overloading through UFCS doesn't work
On 10/17/12 12:46, Timon Gehr wrote: > On 10/15/2012 01:00 PM, Artur Skawina wrote: >> ... >> >> An overloaded operator is just another normal method; you get the same type >> of >> problems when dealing with "normal" methods - eg in types having an "alias >> this" - >> an UFCS "method" must take precedence over one reachable via the alias - >> just like >> in the overloaded op case. The only sane alternative would be disallowing >> UFCS >> for types with an "alias this" (which would be a severe limitation). >> ... > > Just no. Why make symbol lookup even more complicated just in order to > add an strange exception to the language that either enables new forms > of hijacking or would be a severe limitation? Like i said in a later message - I think the /functionality/ should be available, how it's handled is a different issue. The reason why I'd want UFCS take priority over alias-this is that in the presence of multiple aliases and external components method hijacking is actually likely to become a problem. Eg a newer /library/ version can silently hijack an apps UFCS extension "method". Plus, the aliases can severely limit the available ufcs namespace; and the compiler won't even flag many conflicts as errors. UFCS is a much cheaper alternative to sub-typing, having it work for as many cases as possible is important. Think: app that uses an own GUI module on top of a themeing library on top of a GUI widget toolkit on top of a basic windowing system. Being able to extend functionality (by adding just a method or two, in the app) without having to create an extra type hierarchy and dealing with all the conversion issues could be very useful. UFCS is just optional syntax sugar, so I can buy the 'no changes for this' argument - only then I fear that it's usefulness decreases to the point where the cost is no longer justified. For the cases where creating a new type is overkill, one can always use a free function. [Yeah, I'm ignoring the 'functional-style' aspect, I know. UFCS might help there, but a DSL would be better long-term solution IMHO, and would also avoid the @property related issues (extra parens). But that's off-topic, at least in this thread.] artur
Re: Operator overloading through UFCS doesn't work
On 10/17/2012 01:51 PM, Maxim Fomin wrote: On Wednesday, 17 October 2012 at 11:11:26 UTC, Timon Gehr wrote: On 10/14/2012 09:14 AM, Maxim Fomin wrote: On Sunday, 14 October 2012 at 07:01:30 UTC, Tommi wrote: Actually, it seems that alias this has precedence over UFCS. So, a free function opUnary wouldn't ever suit better than an actual method opUnary of the thing referred to by that alias this. http://dpaste.dzfl.pl/d0a4431d Free function doesn't suit better than actual method. The issue is absence of the actual method. opUnary method has priority over alias this, which does make sense because alias this is chosen only when it is impossible to apply operation over A type. If this request is approved and compiler has opUnary definition outside type (which suits better then alias this) such function would hijack alias this. If not, there is a new case when something is going special than at usual cases and only for the purpose of writing operator overloading methods outside the body of the type. The goal must be to get rid of all special behaviour that can result in strange interactions. Add the suitable operator function templates to built-in types. Always rewrite operators to operator function calls. Problem solved. ... You have a struct with alias this to int without overloaded operators. It (say, struct.d) contains code with structure increments. Some other module (say bob.d), which import the structure, defines function supposed to overload opUnary. If operators are always rewritten to function calls, now function should be called in module bob.d, as well as in struct.d if they are compiled together. It certainly not what author of struct.d expected. The case can be even more interesting, if alias this in struct.d were absent at a time when bob.d was written and at some point of future Bob is updating his sources. Members do not cause conflicts with free functions. Alias this takes precedence as it is a member of the type. What is the issue?
Re: Operator overloading through UFCS doesn't work
On 10/17/2012 01:32 PM, Maxim Fomin wrote: On Wednesday, 17 October 2012 at 11:00:05 UTC, Timon Gehr wrote: On 10/16/2012 05:57 PM, Maxim Fomin wrote: ... At NG discussion it may look nice to define some type and then add operator overloading methods Operator overloading is not magic, so your statement can be shortened to ... and then add methods Which is still not correct, because that is not what UFCS does. It is not correct as long as you cavil at lexis, however the statement has room for correction. In a discussion it is crucial that both parties agree on a common set of terms and definitions. Otherwise agreement cannot be detected. but as soon as you import some other modules, authors of which also consider UFCS operators a good idea, Who has stated that? It just does not make sense to explicitly ban them, as they are not special. Who stated that they should be "explicitly banned"? Well, you did, if I got that right: On 10/13/2012 06:02 PM, Maxim Fomin wrote: From my point of view operator overloading methods are special functions and not treating them as candidates for UFCS does make more sense. (Explicitly banning UFCS operators is the same thing as not treating rewritten operators as candidates for UFCS.) I explained potential problem in previous posts. Yes, several times, but to me the argument still looks similar to the following (note that only the 'counter argument' part is related to this discussion): Issue: the 'body' keyword is not used enough to warrant keyword status, parsing could succeed without making it a full keyword. Proposed solution: make 'body' a valid identifier that is only recognised as special where it is expected to occur. Main counter argument: Two people might define a symbol using the newly available 'body' identifier, which can cause symbol conflicts. Do you agree with this reasoning, and if not, what is the fundamental difference between this and the point you are trying to convey? everything breaks including namespace conflict The usual disambiguation procedures apply. (Which are broken in DMD at the moment, because module-scope private symbols can cause conflicts.) Infix operators are not special. It is just notation. as well as loosing ability to manipulate that type within built-in expression as well. I did not get that. Again, the problem is in conflict between different declared operator overloading functions across different modules. Can you explain the usage of the term 'built-in' in this context?
Re: Operator overloading through UFCS doesn't work
On Wednesday, 17 October 2012 at 11:11:26 UTC, Timon Gehr wrote: On 10/14/2012 09:14 AM, Maxim Fomin wrote: On Sunday, 14 October 2012 at 07:01:30 UTC, Tommi wrote: Actually, it seems that alias this has precedence over UFCS. So, a free function opUnary wouldn't ever suit better than an actual method opUnary of the thing referred to by that alias this. http://dpaste.dzfl.pl/d0a4431d Free function doesn't suit better than actual method. The issue is absence of the actual method. opUnary method has priority over alias this, which does make sense because alias this is chosen only when it is impossible to apply operation over A type. If this request is approved and compiler has opUnary definition outside type (which suits better then alias this) such function would hijack alias this. If not, there is a new case when something is going special than at usual cases and only for the purpose of writing operator overloading methods outside the body of the type. The goal must be to get rid of all special behaviour that can result in strange interactions. Add the suitable operator function templates to built-in types. Always rewrite operators to operator function calls. Problem solved. ... You have a struct with alias this to int without overloaded operators. It (say, struct.d) contains code with structure increments. Some other module (say bob.d), which import the structure, defines function supposed to overload opUnary. If operators are always rewritten to function calls, now function should be called in module bob.d, as well as in struct.d if they are compiled together. It certainly not what author of struct.d expected. The case can be even more interesting, if alias this in struct.d were absent at a time when bob.d was written and at some point of future Bob is updating his sources.
Re: Operator overloading through UFCS doesn't work
On Wednesday, 17 October 2012 at 11:00:05 UTC, Timon Gehr wrote: On 10/16/2012 05:57 PM, Maxim Fomin wrote: ... At NG discussion it may look nice to define some type and then add operator overloading methods Operator overloading is not magic, so your statement can be shortened to ... and then add methods Which is still not correct, because that is not what UFCS does. It is not correct as long as you cavil at lexis, however the statement has room for correction. but as soon as you import some other modules, authors of which also consider UFCS operators a good idea, Who has stated that? It just does not make sense to explicitly ban them, as they are not special. Who stated that they should be "explicitly banned"? I explained potential problem in previous posts. everything breaks including namespace conflict The usual disambiguation procedures apply. (Which are broken in DMD at the moment, because module-scope private symbols can cause conflicts.) Infix operators are not special. It is just notation. as well as loosing ability to manipulate that type within built-in expression as well. I did not get that. Again, the problem is in conflict between different declared operator overloading functions across different modules.
Re: Operator overloading through UFCS doesn't work
On 10/14/2012 09:01 AM, Maxim Fomin wrote: On Saturday, 13 October 2012 at 19:50:02 UTC, Timon Gehr wrote: On 10/13/2012 06:02 PM, Maxim Fomin wrote: ... Different groups of people have different mind and same things produce different sense on them. From my point of view operator overloading methods are special functions and not treating them as candidates for UFCS does make more sense. I do not understand how an operation that happens to be called '+' is fundamentally different from an operation that is called 'add'. The first one is an operator, which sometimes, may be rewritten to function call, the second one is a function call. What is the difference between an operator and a function call? Is it important? int add(int a, int b){ return a+b; } // or conversely (not valid syntax): int (int a)+(int b){ return add(a,b); } Even if you convince in your opinion, language addition without applied purposes makes no benefit. I guess the functionality could be achieved in DMD mostly by removing code. (Code for good error messages excluded!) I don't understand what you are trying to say. Anyway, you can write a pull request and propose it at github. It would be interesting to see reaction of others. Built-in types shouldn't be special except maybe that the parser recognises related keywords. It should go like this: a + b => a.opBinary!"+"(b); // opBinary_r woes left out, // but would require treatment a + b => __add(a,b); // if a and b of built-in type a.opBinary!"+"(b) => __add(a,b); // if a and b of built-in type Where __add(a,b) is the representation of an AST node of a built-in add operation involving operands a and b. All the code in DMD that supposedly tries to make up for this kind of inconsistencies (and sometimes fails, because it does not catch all the ways the language features interact) can be gotten rid of.
Re: Operator overloading through UFCS doesn't work
On Wednesday, 17 October 2012 at 10:24:57 UTC, Artur Skawina wrote: Operator overloading can be abused - that's an obvious and well known fact. But that same feature can also be very useful, if used right. Worrying about UFCS problems in the context of op-overloading needlessly complicates the issue - the UFCS problems are there also w/o op-overloads. ... artur I don't see how this addresses the issue I try to put attention to. The discussion it turning into repetition of views.
Re: Operator overloading through UFCS doesn't work
On 10/14/2012 09:14 AM, Maxim Fomin wrote: On Sunday, 14 October 2012 at 07:01:30 UTC, Tommi wrote: Actually, it seems that alias this has precedence over UFCS. So, a free function opUnary wouldn't ever suit better than an actual method opUnary of the thing referred to by that alias this. http://dpaste.dzfl.pl/d0a4431d Free function doesn't suit better than actual method. The issue is absence of the actual method. opUnary method has priority over alias this, which does make sense because alias this is chosen only when it is impossible to apply operation over A type. If this request is approved and compiler has opUnary definition outside type (which suits better then alias this) such function would hijack alias this. If not, there is a new case when something is going special than at usual cases and only for the purpose of writing operator overloading methods outside the body of the type. The goal must be to get rid of all special behaviour that can result in strange interactions. Add the suitable operator function templates to built-in types. Always rewrite operators to operator function calls. Problem solved. (And people are already asking for custom constant folding procedures, even without having this in place to use as a supporting argument.)
Re: Operator overloading through UFCS doesn't work
On 10/16/2012 05:57 PM, Maxim Fomin wrote: ... At NG discussion it may look nice to define some type and then add operator overloading methods Operator overloading is not magic, so your statement can be shortened to ... and then add methods Which is still not correct, because that is not what UFCS does. but as soon as you import some other modules, authors of which also consider UFCS operators a good idea, Who has stated that? It just does not make sense to explicitly ban them, as they are not special. everything breaks including namespace conflict The usual disambiguation procedures apply. (Which are broken in DMD at the moment, because module-scope private symbols can cause conflicts.) Infix operators are not special. It is just notation. as well as loosing ability to manipulate that type within built-in expression as well. I did not get that.
Re: Operator overloading through UFCS doesn't work
On 10/15/2012 01:00 PM, Artur Skawina wrote: ... An overloaded operator is just another normal method; you get the same type of problems when dealing with "normal" methods - eg in types having an "alias this" - an UFCS "method" must take precedence over one reachable via the alias - just like in the overloaded op case. The only sane alternative would be disallowing UFCS for types with an "alias this" (which would be a severe limitation). ... Just no. Why make symbol lookup even more complicated just in order to add an strange exception to the language that either enables new forms of hijacking or would be a severe limitation?
Re: Operator overloading through UFCS doesn't work
On 10/16/12 17:57, Maxim Fomin wrote: > This doesn't scale well. Certanly, nobody would intentionally create > problems. But if you import code of other people, whom you cannot control, > override problems occurs. > > At NG discussion it may look nice to define some type and then add operator > overloading methods but as soon as you import some other modules, authors of > which also consider UFCS operators a good idea, everything breaks including > namespace conflict as well as loosing ability to manipulate that type within > built-in expression as well. Operator overloading can be abused - that's an obvious and well known fact. But that same feature can also be very useful, if used right. Worrying about UFCS problems in the context of op-overloading needlessly complicates the issue - the UFCS problems are there also w/o op-overloads. UFCS is, like a lot of D features, just a quick hack, that somebody thought was "cute" enough to add to the language, w/o really considering the consequences. It might be possible to make UFCS solid enough, but introducing a lot of special-casing isn't the right way. For example, UFCS will allow you to write a templated generic function (or new type) that works with any type providing a certain interface (function signatures in this case) and "fills in" missing optional methods, w/o cluttering the code or wrapping the methods, right? Wrong, as UFCS is (wrongly) restricted to module-scope functions, hence can't be used for this. UFCS is nothing but syntax sugar (you can do 'f(o)' everywhere where UFCS would let you do 'o.f()'). Similarly as in the op-overload case - if the argument is that lifting the restriction would allow for confusing code (methods being available in one scope, but not another; or 'o.f()' doing different things in different scopes) then that's an argument for *removing UFCS* - as the same problems happen elsewhere too (think two modules each containing a different 'f(o)' function definition and several scopes that locally import just one of the modules). Things would be a bit different if D had a "global" scope, but that would also add new issues, and UFCS alone does not justify introducing one. (It wouldn't even help UFCS much, other than reducing the chances of clashing method names) artur
Re: Operator overloading through UFCS doesn't work
On Tuesday, 16 October 2012 at 16:10:31 UTC, Tommi wrote: On Monday, 15 October 2012 at 09:33:23 UTC, Maxim Fomin wrote: ---foo.d--- struct A { int i; alias i this; } ---bar.d--- int opUnary(string T)(A a) { ... } ... { ++a; } --- I. i is incremented, opUnary is not called. However opUnary matches better to the actual type and if it were a method, it would be called - another special issue in the language which breaks usual logic. And if you declared opUnary in bar.d when alias this was absent in foo.d and later added - hijacking also occurs but now it happens from another side. Bad. Let's talk about the semantics of the word "hijacking" as it relates to this discussion. Here's my take on it: Let type T have some inherent functionality F. That is, functionality F cannot be removed from T without making changes to the module file where type T is defined. Then, if some other functionality F' overrides (replaces and modifies) F, it is said that F' "hijacks" F. If I apply this definition to your example, we see that the free function opUnary in bar.d is *not* part of struct A's inherent functionality. Therefore, by adding later that alias this in A's definition, we are *not* hijacking anything. Furthermore, if that free function opUnary was defined in foo.d instead, it would be an inherent part of A's functionality. Then, by adding later that alias this in A's definition, we would be *changing* A's functionality. But that's not hijacking, because we're making the change in the module file where A is defined. That's not hijacking, that's just changing the inherent functionality of your own user-defined type. Or simpler: hijack is when some code is written and under new circumstances the same code does different things. Hijack happens if there are two conflict entities and one of them has higher priority than another. In case of UFCS depending on design two types of hijacks may happen: - either newly created free UFCS function hijacks existing method - or newly created method hijacks existing free function. Currently D is designed in favor for blocking first type which doesn't mean that it stops author of some type from declaring new methods and breaking (hijacking) free functions.
Re: Operator overloading through UFCS doesn't work
On Monday, 15 October 2012 at 09:33:23 UTC, Maxim Fomin wrote: ---foo.d--- struct A { int i; alias i this; } ---bar.d--- int opUnary(string T)(A a) { ... } ... { ++a; } --- I. i is incremented, opUnary is not called. However opUnary matches better to the actual type and if it were a method, it would be called - another special issue in the language which breaks usual logic. And if you declared opUnary in bar.d when alias this was absent in foo.d and later added - hijacking also occurs but now it happens from another side. Bad. Let's talk about the semantics of the word "hijacking" as it relates to this discussion. Here's my take on it: Let type T have some inherent functionality F. That is, functionality F cannot be removed from T without making changes to the module file where type T is defined. Then, if some other functionality F' overrides (replaces and modifies) F, it is said that F' "hijacks" F. If I apply this definition to your example, we see that the free function opUnary in bar.d is *not* part of struct A's inherent functionality. Therefore, by adding later that alias this in A's definition, we are *not* hijacking anything. Furthermore, if that free function opUnary was defined in foo.d instead, it would be an inherent part of A's functionality. Then, by adding later that alias this in A's definition, we would be *changing* A's functionality. But that's not hijacking, because we're making the change in the module file where A is defined. That's not hijacking, that's just changing the inherent functionality of your own user-defined type.
Re: Operator overloading through UFCS doesn't work
On Tuesday, 16 October 2012 at 00:50:54 UTC, Artur Skawina wrote: Actually, I'm not really in any camp. UFCS has several obvious problems plus likely quite a few more subtle ones. Ignoring the issues does not make them go away and the some-compiler-happens-to-implement-it-like-that-today-therefore-thats-how-it- -must-work arguments, that often appear here, do not really help. I am against proposal not because dmd doesn't do it but because I believe it is unwisely pushed in a manner that defects the purpose of proposal. BTW, some operators are not overloadable because, if I remember right, "they were considered to create more confusion than flexibility if ever overloaded". Leaving UFCS with operator overloading as they are now does make sense, because of similar reason. Well, i don't think anybody wants to /override/ existing operators - it's only about allowing /extending/ the functionality of non-local types, by adding support for additional ops and/or types. While being able to override existing methods with UFCS would have some uses, allowing that would also introduce additional problems. Anyway, if you need to modify how a type's existing ops work you can always sub-type - this is also true in the (non-virtual) method case; UFCS is basically just syntax sugar (which allows you to transparently locally inject methods w/o creating a new type, downcasting and handling the (implicit) upcasts (which could be problematic when eg working with (pointers-to-)structs)). artur This doesn't scale well. Certanly, nobody would intentionally create problems. But if you import code of other people, whom you cannot control, override problems occurs. At NG discussion it may look nice to define some type and then add operator overloading methods but as soon as you import some other modules, authors of which also consider UFCS operators a good idea, everything breaks including namespace conflict as well as loosing ability to manipulate that type within built-in expression as well.
Re: Operator overloading through UFCS doesn't work
On 10/15/12 17:49, Maxim Fomin wrote: > On Monday, 15 October 2012 at 11:01:13 UTC, Artur Skawina wrote: >> UFCS has pros and cons. I could agree that it has problems and should be >> removed >> from the language completely. But if the feature is there, it should work, >> w/o any >> unnecessary special cases. > > Special cases would be created by any decision, the only question is which > feature is discriminated - alias this, UFCS or something else (currently UFCS > is). > >> An overloaded operator is just another normal method; you get the same type >> of >> problems when dealing with "normal" methods - eg in types having an "alias >> this" - >> an UFCS "method" must take precedence over one reachable via the alias - >> just like >> in the overloaded op case. The only sane alternative would be disallowing >> UFCS >> for types with an "alias this" (which would be a severe limitation). > > You seem to be in the second camp (UFCS free function takes precedence over > alias this, if declared). I am not against, just to note. Actually, I'm not really in any camp. UFCS has several obvious problems plus likely quite a few more subtle ones. Ignoring the issues does not make them go away and the some-compiler-happens-to-implement-it-like-that-today-therefore-thats-how-it- -must-work arguments, that often appear here, do not really help. Note that my above "UFCS method must take precedence" statement only describes the required functionality; handling it like that /by default/ wouldn't probably be a good idea, as that would make accidental method hijacking possible. The lookup should be more like - T.method - ufcs_method(T) marked with 'override' - while (T = alias this) { - T.method - ufcs_method(T) marked with 'override' } - ufcs_method(T) w/o 'override' - while (T = alias this) - ufcs_method(T) w/o 'override' with the compiler enforcing the obvious 'override' rules for ufcs_method declarations (requiring that T isn't opaque when declaring (or calling) UFCS functions is reasonable, i don't think having 'foo(a)' and 'a.foo()' mean completely different things would work well together with UFCS). And yes, it wouldn't completely eliminate the possibility of hijacking - but you'd need three components interacting for it to happen, which would make it much less likely to occur. [Note i came up w/ this design while writing this email - it's not necessarily perfect.] >> And the purpose of UFCS is?... "operator overloading methods" are /not/ >> special. > > The point is that when you want to define UFCS free functions like opUnary, > you want not only to call them like a.opUnary!"++"() but to code like ++a. > That is the key issue here and that makes the whole case special. > > In other words: with UFCS you have an option: to call your function as it was > a method of some type. And anyone has this option. The only problem is > namespace conflict which can be easily avoided. But you still has the option. > With UFCS operator overloaded functions you have *two* options: to call free > functions as methods as usual *and* to use struct/class with many operators > in a manner you want. But if anyone of that type users define his set of > operator overloaded functions *you lose the second option* which makes the > proposal to allow simultaneous access to single resource pointless. This is how UFCS works - for "normal" methods. There's no reason to handle op overloads or other special methods differently. You are arguing for introducing arbitrary restrictions, which would bring no gain (that i can see right now), but disallow useful functionality. Yes, there are some issues, but those are not op-overload specific, but UFCS-related. > Yes, it also may happen with regular function, when you lose ability to give > a function some specific name you want (like "create", "foo" etc.). But in > case of UFCS operators you lose not only some function name ("opUnary") but > corresponding expression as well (++). > This means that it makes sense to allow only one set of opUnary/opBinary/.. > etc. of functions (anyway, only one can define them and use with operators) > and the most suitable place is declaration of their type. Well, i don't think anybody wants to /override/ existing operators - it's only about allowing /extending/ the functionality of non-local types, by adding support for additional ops and/or types. While being able to override existing methods with UFCS would have some uses, allowing that would also introduce additional problems. Anyway, if you need to modify how a type's existing ops work you can always sub-type - this is also true in the (non-virtual) method case; UFCS is basically just syntax sugar (which allows you to transparently locally inject methods w/o creating a new type, downcasting and handling the (implicit) upcasts (which could be problematic when eg working with (pointers-to-)structs)). artur
Re: Operator overloading through UFCS doesn't work
On Monday, 15 October 2012 at 11:01:13 UTC, Artur Skawina wrote: UFCS has pros and cons. I could agree that it has problems and should be removed from the language completely. But if the feature is there, it should work, w/o any unnecessary special cases. Special cases would be created by any decision, the only question is which feature is discriminated - alias this, UFCS or something else (currently UFCS is). An overloaded operator is just another normal method; you get the same type of problems when dealing with "normal" methods - eg in types having an "alias this" - an UFCS "method" must take precedence over one reachable via the alias - just like in the overloaded op case. The only sane alternative would be disallowing UFCS for types with an "alias this" (which would be a severe limitation). You seem to be in the second camp (UFCS free function takes precedence over alias this, if declared). I am not against, just to note. And the purpose of UFCS is?... "operator overloading methods" are /not/ special. artur The point is that when you want to define UFCS free functions like opUnary, you want not only to call them like a.opUnary!"++"() but to code like ++a. That is the key issue here and that makes the whole case special. In other words: with UFCS you have an option: to call your function as it was a method of some type. And anyone has this option. The only problem is namespace conflict which can be easily avoided. But you still has the option. With UFCS operator overloaded functions you have *two* options: to call free functions as methods as usual *and* to use struct/class with many operators in a manner you want. But if anyone of that type users define his set of operator overloaded functions *you lose the second option* which makes the proposal to allow simultaneous access to single resource pointless. Consider this: ---somelib.d--- struct A { void foo() {} } ---otherlib.d--- void bar(A a) {} ---mycode.d--- // blah, foo and bar are taken // solution - choose other name void baz(A a) {} --- Now assume, UFCS operator overload is possible. ---somelib.d--- struct A { int i; int j; } ---mycode.d--- int opUnary(string T : "++")() { return ++i; } ... ++a; ... - At some point of time the owner of somelib.d changes code (or anyone whom code you import define such functions): ---somelib.d--- struct A { int i; int j; int opUnary(string T : "++")() { return ++j; } } ---mycode.d--- int opUnary(string T : "++")() { return ++i; } So, you lost your option to use A in expressions and call your function which is the point here. You cannot invent +my_unary+ operator. Neither you can rebind ++ to some function other than opUnary. Yes, it also may happen with regular function, when you lose ability to give a function some specific name you want (like "create", "foo" etc.). But in case of UFCS operators you lose not only some function name ("opUnary") but corresponding expression as well (++). This means that it makes sense to allow only one set of opUnary/opBinary/.. etc. of functions (anyway, only one can define them and use with operators) and the most suitable place is declaration of their type.
Re: Operator overloading through UFCS doesn't work
On 10/15/12 10:17, Maxim Fomin wrote: > On Sunday, 14 October 2012 at 19:50:54 UTC, Artur Skawina wrote: >> On 10/14/12 08:13, Maxim Fomin wrote: >>> The only mentioned reason is to allow writing operator overloading methods >>> outside type scope - just because somebody (currently two people) consider >>> it logical to broaden UFCS usage. >> >> It's more than two people... Also, it's not about "broadening UFCS usage", >> it's about making UFCS work properly. > > When UFCS was added to the language its purpose was to call free functions > pretending you are invoking methods. It does it job pretty well and actually > works properly. But some questions arise: how this addition interacts with > other parts of the language: > - with calling through pointer (8603) > - with template alias parameters (8692) > - with function imports (6185) > - with typeof operator (8661) > - with operator overloading > - ... > - probably other issues which are not encountered yet. UFCS has pros and cons. I could agree that it has problems and should be removed from the language completely. But if the feature is there, it should work, w/o any unnecessary special cases. An overloaded operator is just another normal method; you get the same type of problems when dealing with "normal" methods - eg in types having an "alias this" - an UFCS "method" must take precedence over one reachable via the alias - just like in the overloaded op case. The only sane alternative would be disallowing UFCS for types with an "alias this" (which would be a severe limitation). > Each time there should be a decision to choose which language feature has > higher priority. That is why this is broadening UFCS usage on areas of the > language where it has never been before rather than making work properly in > existing areas of usage. > >>> This doesn't solve ay practical issue. >> >> Obviously, it does. Otherwise this issue wouldn't come up repeatedly. > > Actually not - the only purpose mentioned in the thread was to place operator > overloading methods outside scope of declaration. And the purpose of UFCS is?... "operator overloading methods" are /not/ special. There have been several threads in the past where this missing functionality was mentioned. It's how it should work, if UFCS is here to stay. If you think that would causes problems and UFCS should instead be removed from the language then I can understand that - UFCS /does/ have issues. But it's also useful and I'm not yet convinced that the problems it introduces justifies killing the feature. Nothing justifies special casing just certain combinations like op-overloading and UFCS however, especially when it's not necessary. artur
Re: Operator overloading through UFCS doesn't work
On Sunday, 14 October 2012 at 20:15:15 UTC, Tommi wrote: On Sunday, 14 October 2012 at 07:14:25 UTC, Maxim Fomin wrote: If this request is approved and compiler has opUnary definition outside type (which suits better then alias this) such function would hijack alias this. Free functions cannot and must not ever hijack, i.e. modify existing functionality of a type. Free functions should only be able to add new functionality to a type. This is what currently happens with alias this vs free function which is accessed through UFCS: This shows current behavior. The issue is future behavior of code like this: ---foo.d--- struct A { int i; alias i this; } ---bar.d--- int opUnary(string T)(A a) { ... } ... { ++a; } --- I. i is incremented, opUnary is not called. However opUnary matches better to the actual type and if it were a method, it would be called - another special issue in the language which breaks usual logic. And if you declared opUnary in bar.d when alias this was absent in foo.d and later added - hijacking also occurs but now it happens from another side. Bad. II. opUnary is called, i is not incremented. On the one hand you get what you wanted - you supplied opUnary and it is called. At least this is consistent with matching behavior. On the other hand, it is hijacking from position of foo. Bad. III. Compiler issues error if you try to define some free functions which are similar to existing methods (code above is particular case) or if you declare operator overloading methods in the presence of alias this. This prevents from making confusion but if you link to some library, update code and its author defines new method, which is similar to your UFCS function, you get errors and have to rewrite code. IV. Do nothing and leave things as they are. Presence of opUnary function doesn't affect operator overloading. While current UFCS behavior falls in the first category (newly created foo.d methods hijack bar's free functions) there are no such problems with operator overloading methods. And operator overloading requires methods, not just free functions. Although methods and free functions may be called similar in source code, they still are different - in runtime calling, in mangling, in requiring contract invocation, argument passing, etc.
Re: Operator overloading through UFCS doesn't work
On Sunday, 14 October 2012 at 19:50:54 UTC, Artur Skawina wrote: On 10/14/12 08:13, Maxim Fomin wrote: The only mentioned reason is to allow writing operator overloading methods outside type scope - just because somebody (currently two people) consider it logical to broaden UFCS usage. It's more than two people... Also, it's not about "broadening UFCS usage", it's about making UFCS work properly. When UFCS was added to the language its purpose was to call free functions pretending you are invoking methods. It does it job pretty well and actually works properly. But some questions arise: how this addition interacts with other parts of the language: - with calling through pointer (8603) - with template alias parameters (8692) - with function imports (6185) - with typeof operator (8661) - with operator overloading - ... - probably other issues which are not encountered yet. Each time there should be a decision to choose which language feature has higher priority. That is why this is broadening UFCS usage on areas of the language where it has never been before rather than making work properly in existing areas of usage. This doesn't solve ay practical issue. Obviously, it does. Otherwise this issue wouldn't come up repeatedly. artur Actually not - the only purpose mentioned in the thread was to place operator overloading methods outside scope of declaration.
Re: Operator overloading through UFCS doesn't work
On Sunday, 14 October 2012 at 07:14:25 UTC, Maxim Fomin wrote: If this request is approved and compiler has opUnary definition outside type (which suits better then alias this) such function would hijack alias this. Free functions cannot and must not ever hijack, i.e. modify existing functionality of a type. Free functions should only be able to add new functionality to a type. This is what currently happens with alias this vs free function which is accessed through UFCS: struct B { void fun() { writeln("B.fun()"); } } struct A { B b; alias b this; } void fun(A a) { writeln(".fun(A)"); } void main() { A a; a.fun(); // prints B.fun() as it should } It shouldn't be any different if fun was some operator function, like opUnary; the free function mustn't hijack type A's existing functionality (which is currently being provided to A by that alias this thingy).
Re: Operator overloading through UFCS doesn't work
On 10/14/12 08:13, Maxim Fomin wrote: > The only mentioned reason is to allow writing operator overloading methods > outside type scope - just because somebody (currently two people) consider it > logical to broaden UFCS usage. It's more than two people... Also, it's not about "broadening UFCS usage", it's about making UFCS work properly. > This doesn't solve ay practical issue. Obviously, it does. Otherwise this issue wouldn't come up repeatedly. artur
Re: Operator overloading through UFCS doesn't work
On Sunday, 14 October 2012 at 07:01:30 UTC, Tommi wrote: Actually, it seems that alias this has precedence over UFCS. So, a free function opUnary wouldn't ever suit better than an actual method opUnary of the thing referred to by that alias this. http://dpaste.dzfl.pl/d0a4431d Free function doesn't suit better than actual method. The issue is absence of the actual method. opUnary method has priority over alias this, which does make sense because alias this is chosen only when it is impossible to apply operation over A type. If this request is approved and compiler has opUnary definition outside type (which suits better then alias this) such function would hijack alias this. If not, there is a new case when something is going special than at usual cases and only for the purpose of writing operator overloading methods outside the body of the type.
Re: Operator overloading through UFCS doesn't work
On Saturday, 13 October 2012 at 19:50:02 UTC, Timon Gehr wrote: On 10/13/2012 06:02 PM, Maxim Fomin wrote: ... Different groups of people have different mind and same things produce different sense on them. From my point of view operator overloading methods are special functions and not treating them as candidates for UFCS does make more sense. I do not understand how an operation that happens to be called '+' is fundamentally different from an operation that is called 'add'. The first one is an operator, which sometimes, may be rewritten to function call, the second one is a function call. Even if you convince in your opinion, language addition without applied purposes makes no benefit. I guess the functionality could be achieved in DMD mostly by removing code. (Code for good error messages excluded!) I don't understand what you are trying to say. Anyway, you can write a pull request and propose it at github. It would be interesting to see reaction of others.
Re: Operator overloading through UFCS doesn't work
On Sunday, 14 October 2012 at 06:22:03 UTC, Maxim Fomin wrote: On Saturday, 13 October 2012 at 17:01:27 UTC, Tommi wrote: Another way to describe my reasoning... According to TDPL, if var is a variable of a user-defined type, then: ++var gets rewritten as: var.opUnary!"++"() Not always. If user-defined type has an alias this to integer member, than something different would happen. Yeah, I wasn't specific enough with that example. It would be also interesting to see, how operation ++T would differ because somebody imported module with opUnary method. Because opUnary suits better than alias this, dmd will issue call to that function, it it see its declaration. Actually, it seems that alias this has precedence over UFCS. So, a free function opUnary wouldn't ever suit better than an actual method opUnary of the thing referred to by that alias this.
Re: Operator overloading through UFCS doesn't work
On Saturday, 13 October 2012 at 17:01:27 UTC, Tommi wrote: Another way to describe my reasoning... According to TDPL, if var is a variable of a user-defined type, then: ++var gets rewritten as: var.opUnary!"++"() Not always. If user-defined type has an alias this to integer member, than something different would happen. It would be also interesting to see, how operation ++T would differ because somebody imported module with opUnary method. Because opUnary suits better than alias this, dmd will issue call to that function, it it see its declaration.
Re: Operator overloading through UFCS doesn't work
On Saturday, 13 October 2012 at 22:34:19 UTC, H. S. Teoh wrote: OK, before this thread devolves into a shouting match, I'd like to understand what was the rationale behind this restriction. What were the reasons behind not allowing a non-member function to overload an operator? What are the pros and cons considered at the time, and how do they weigh now? Or was it just a matter of not being implemented because nobody thought about it at the time? T It likely was not implemented rather than disallowed. The only mentioned reason is to allow writing operator overloading methods outside type scope - just because somebody (currently two people) consider it logical to broaden UFCS usage. This doesn't solve ay practical issue.
Re: Operator overloading through UFCS doesn't work
On 10/14/2012 12:36 AM, H. S. Teoh wrote: On Sun, Oct 14, 2012 at 12:12:01AM +0200, Timon Gehr wrote: On 10/13/2012 10:15 PM, Jonathan M Davis wrote: [...] but they _aren't_ mixed and they will _never_ be mixed. If it had _ever_ been intended that it be possible to overload operators as free functions, then we'd simply have made it so that you could declare a free function like auto opBinary(string op)(Foo foo, Bar bar) { ... } in the first place without requiring that it be a member function. You can. But it _was_ required to be a member function, and it would make no sense to allow a new feature to circumvent that restriction. If it was supposed to be circumventable, then the restriction wouldn't have been put there in the first place. This argument is even less convincing in the context of an informally specified language with a somewhat buggy reference compiler. OK, before this thread devolves into a shouting match, I'd like to understand what was the rationale behind this restriction. What were the reasons behind not allowing a non-member function to overload an operator? What are the pros and cons considered at the time, and how do they weigh now? Or was it just a matter of not being implemented because nobody thought about it at the time? T Afaik free-function operator overloads (but not in the context of UFCS) were considered and turned down because D did not want to get amidst discussions about adding Koenig lookup. UFCS does not do Koenig lookup.
Re: Operator overloading through UFCS doesn't work
On Sun, Oct 14, 2012 at 12:12:01AM +0200, Timon Gehr wrote: > On 10/13/2012 10:15 PM, Jonathan M Davis wrote: [...] > >but they _aren't_ mixed and they will _never_ be mixed. If it had > >_ever_ been intended that it be possible to overload operators as > >free functions, then we'd simply have made it so that you could > >declare a free function like > > > >auto opBinary(string op)(Foo foo, Bar bar) > >{ > >... > >} > > > >in the first place without requiring that it be a member function. > > You can. > > >But it _was_ required to be a member function, and it would make no > >sense to allow a new feature to circumvent that restriction. If it > >was supposed to be circumventable, then the restriction wouldn't have > >been put there in the first place. > > This argument is even less convincing in the context of an informally > specified language with a somewhat buggy reference compiler. OK, before this thread devolves into a shouting match, I'd like to understand what was the rationale behind this restriction. What were the reasons behind not allowing a non-member function to overload an operator? What are the pros and cons considered at the time, and how do they weigh now? Or was it just a matter of not being implemented because nobody thought about it at the time? T -- Why can't you just be a nonconformist like everyone else? -- YHL
Re: Operator overloading through UFCS doesn't work
On 10/13/2012 10:15 PM, Jonathan M Davis wrote: ... construct is using syntactic sugar such as UFCS, because _all_ of that syntactic sugar must be lowered to code which _isn't_ syntactic sugar anymore. That is not what lowering means. It would be far more expensive to have to continually make passes to lower code over and over again until no more lowering was required than it would be to just have to lower it once. ... It does not have to be implemented it in an inefficient way. It is actually simpler for a sane compiler implementation to make UFCS apply to lowered overloaded operators than to restrict it. You're reading way to much into what TDPL is saying. It's simply telling you about how the compiler goes about translating code which uses operators such as +, >, or = into the functions that you used to overload them. It's _not_ telling you that it'll do UFCS on overloaded operator functions. It is telling us that a+b is transformed to a.opBinary!"+"(b) UFCS applies to a.opBinary!"+"(b). Heck, technically, TDPL never really says that D _has_ UFCS. It talks about the member call function syntax for _arrays_ (which D had for ages before it had UFCS), not for types in general. It's only very recently that full UFCS has been added to the language. Exactly, so what is the point? If TDPL does not talk about the UFCS feature, then TDPL not talking about UFCS in the context of one specific case certainly cannot be used as an argument to justify that it should not apply in that case. Both overloaded operators and UFCS use lowering to generate different code which the compiler then compiles, By using the same strategy recursively, otherwise it is not called lowering. but they _aren't_ mixed and they will _never_ be mixed. If it had _ever_ been intended that it be possible to overload operators as free functions, then we'd simply have made it so that you could declare a free function like auto opBinary(string op)(Foo foo, Bar bar) { ... } in the first place without requiring that it be a member function. You can. But it _was_ required to be a member function, and it would make no sense to allow a new feature to circumvent that restriction. If it was supposed to be circumventable, then the restriction wouldn't have been put there in the first place. This argument is even less convincing in the context of an informally specified language with a somewhat buggy reference compiler.
Re: Operator overloading through UFCS doesn't work
On Saturday, October 13, 2012 19:01:26 Tommi wrote: > Another way to describe my reasoning... > > According to TDPL, if var is a variable of a user-defined type, > then: > ++var > gets rewritten as: > var.opUnary!"++"() > > Thus, it would be very logical to assume that it doesn't matter > whether you write: > ++var > ...or, write the following instead: > var.opUnary!"++"() > ...because the second form is what the first form gets written to > anyway. > > But, that "very logical assumption" turns out to be wrong. > Because in D, as it stands currently, it *does* make a difference > whether you write it using the first form or the second: > > struct S > { > int _value; > } > > ref S opUnary(string op : "++")(ref S s) > { > ++s._value; > return s; > } > > Now, writing the following compiles and works: > S var; > var.opUnary!"++"(); > > ...while the following doesn't compile: > S var; > ++var; > > This behavior of the language is not logical. And I don't think > that logic is a matter of preference or taste. All that TDPL is telling you is that the compiler uses "lowering" on overloaded operators to generate their code. It lowers them to calls the appropriate functions. But it does so in a way consistent with how the operators are supposed to work. Preincrement and Postincrement are fundamentally different. D makes the wise choice of making it so that you simply overload increment and then has the compiler use that function in a manner that generates code which is preincrementing or postincrementing depending on which operator was used. This guarantees that preincrement and postincrement are consistent. This is in contrast to C++ where you could make them do totally different things. Not only does that avoid weird bugs, but it makes it so that the compiler can generate more efficient code too. This is because postincrementing generates a temporary to save the original value for the expression where it's being used whereas preincrement does not, and if the compiler knows that preincrement and postincrement are semantically the same, then it can replace postincrement with preincrement when it doesn't matter which is called. In D, because you overload _one_ operator, the compiler knows this for user-defined types, but in C++, it doesn't, and can't make that optimization. So, in C++, code like for(vector::iterator i = v.begin(), e = v.end; i != e; i++) {} is stuck creating a temporary for every call to i++, whereas in D, it can be replaced with ++i. Similarly, in D, >, >=, <=, and < are all translated to calls to opCmp, making it so that you overload one function but get 4 operators. There are cases where it would just be broken for the compiler to simply call your overloaded operator function without doing extra stuff to ensure that it acted like the built-in operators (incrementing being a prime example). So no, it's _not_ simply a matter of calling your overloaded operator functions. It's just that part of the process of compiling code using overloaded operators is to translate it to code which involves calling the overloaded operator functions. That translation may or may not be direct. TDPL makes a point about it to show that the compiler is able to translate the overloaded operators into function calls and the compile those instead of having to go to all of the extra effort required to deal with fully compiling the overloaded operators directly. It's just much simpler to turn one language construct into another, existing language construct, and then compile that rather than having to understand how to compile both. The same happens with other language constructs as well (e.g. scope statements). TDPL _never_ says that syntactic sugar is applicable to lowered code. Lowering code is effectively an implementation detail of the compiler that makes its life easier. It does _not_ make it so that one language construct will be translated into another where it will then be assumed that the new language construct is using syntactic sugar such as UFCS, because _all_ of that syntactic sugar must be lowered to code which _isn't_ syntactic sugar anymore. It would be far more expensive to have to continually make passes to lower code over and over again until no more lowering was required than it would be to just have to lower it once. You're reading way to much into what TDPL is saying. It's simply telling you about how the compiler goes about translating code which uses operators such as +, >, or = into the functions that you used to overload them. It's _not_ telling you that it'll do UFCS on overloaded operator functions. Heck, technically, TDPL never really says that D _has_ UFCS. It talks about the member call function syntax for _arrays_ (which D had for ages before it had UFCS), not for types in general. It's only very recently that full UFCS has been added to the language. Both overloaded operators and UFCS use lowering to generate differe
Re: Operator overloading through UFCS doesn't work
On 10/13/2012 06:02 PM, Maxim Fomin wrote: ... Different groups of people have different mind and same things produce different sense on them. From my point of view operator overloading methods are special functions and not treating them as candidates for UFCS does make more sense. I do not understand how an operation that happens to be called '+' is fundamentally different from an operation that is called 'add'. Even if you convince in your opinion, language addition without applied purposes makes no benefit. I guess the functionality could be achieved in DMD mostly by removing code. (Code for good error messages excluded!)
Re: Operator overloading through UFCS doesn't work
Another way to describe my reasoning... According to TDPL, if var is a variable of a user-defined type, then: ++var gets rewritten as: var.opUnary!"++"() Thus, it would be very logical to assume that it doesn't matter whether you write: ++var ...or, write the following instead: var.opUnary!"++"() ...because the second form is what the first form gets written to anyway. But, that "very logical assumption" turns out to be wrong. Because in D, as it stands currently, it *does* make a difference whether you write it using the first form or the second: struct S { int _value; } ref S opUnary(string op : "++")(ref S s) { ++s._value; return s; } Now, writing the following compiles and works: S var; var.opUnary!"++"(); ...while the following doesn't compile: S var; ++var; This behavior of the language is not logical. And I don't think that logic is a matter of preference or taste.
Re: Operator overloading through UFCS doesn't work
On Saturday, 13 October 2012 at 16:02:25 UTC, Maxim Fomin wrote: From my point of view operator overloading methods are special functions and not treating them as candidates for UFCS does make more sense. I can think of only one thing that makes custom operator methods "special" or different from regular methods. It's the fact that you don't *have* to call them through the normal method invocation syntax: var.opSomething(...), but rather, the language provides this nice layer of syntactic sugar through which you *can* call those methods, if you so choose to. What you're saying is, that calling those operator methods through this layer of syntactic sugar, e.g. var + 3, is somehow fundamentally different from directly calling the method, to which this layer of syntactic sugar would forward the expression to call anyway, i.e. var.opBinary!"+"(3)
Re: Operator overloading through UFCS doesn't work
On Saturday, 13 October 2012 at 15:06:14 UTC, Tommi wrote: On Saturday, 13 October 2012 at 11:50:40 UTC, Maxim Fomin wrote: I think implementing UFCS operator overloading is problematic. Firstly, you want to put this language addition too far. I don't see this as taking UFCS functionality "further". Rather, I think it's simply more logical that with UFCS you could provide extra operator methods just like you can provide extra "regular" methods. I assumed that UFCS operator overloading would work for sure, and it seems arbitrary to me that it doesn't. I don't consider request to put the language in consistency with superficial logic (UFCS allows to use free functions as methods => allow operator overloading hijacking too) as a rational one. Secondly, compiler needs to know whether operator was overloaded or not. If it knows, it generates code to call "opSomething", if nor - it just doesn't generate anything. Now, imagine what would happen if you write in some module "free" function, supposed to hijack operator overloading method of class or struct in another module. If you compile them together, operator would be overloaded, if separately - nothing would happen. This means that operator overloading would depend on with what you compile your module - sometimes nothing would be overloaded, sometimes it would be with one function, sometimes with another. You use the word "hijack", but free functions can't hijack anything. Their inability to hijack actual methods is not an issue. They can only provide new functionality. The situation you describe is exactly parallel to using UFCS (with regular functions) like this: //File: mystruct.d module mystruct; struct MyStruct { int _value; } //File: incr1.d module incr1; import mystruct; void incr(ref MyStruct ms) { ms._value += 1; } //File: incr2.d module incr2; import mystruct; void incr(ref MyStruct ms) { ms._value += 2; } //File: main.d module main; import std.stdio; import mystruct; static if (true) // change to false to print "2" import incr1; else import incr2; void main() { MyStruct ms; ms.incr(); writeln(ms._value); // prints "1" } Yes. Do you want to have this with operator overloading too? UFCS is useful if you import some library and want to add extra functionality to particular needs of a module. So, if you do this multiple times and merge modules you may not get into trouble with high probability of conflicting names. Even if they conflict, you can slightly rename them. Can you do this if you define in several modules different operator overloading methods? Guess not. Thirdly, I see no reason in allowing it - for what purpose does you proposal service for? The main reason to me is that it would make more sense. It'd seem more logical that way. I can't think of any use cases. Different groups of people have different mind and same things produce different sense on them. From my point of view operator overloading methods are special functions and not treating them as candidates for UFCS does make more sense. Even if you convince in your opinion, language addition without applied purposes makes no benefit.
Re: Operator overloading through UFCS doesn't work
On Saturday, 13 October 2012 at 11:50:40 UTC, Maxim Fomin wrote: I think implementing UFCS operator overloading is problematic. Firstly, you want to put this language addition too far. I don't see this as taking UFCS functionality "further". Rather, I think it's simply more logical that with UFCS you could provide extra operator methods just like you can provide extra "regular" methods. I assumed that UFCS operator overloading would work for sure, and it seems arbitrary to me that it doesn't. Secondly, compiler needs to know whether operator was overloaded or not. If it knows, it generates code to call "opSomething", if nor - it just doesn't generate anything. Now, imagine what would happen if you write in some module "free" function, supposed to hijack operator overloading method of class or struct in another module. If you compile them together, operator would be overloaded, if separately - nothing would happen. This means that operator overloading would depend on with what you compile your module - sometimes nothing would be overloaded, sometimes it would be with one function, sometimes with another. You use the word "hijack", but free functions can't hijack anything. They can only provide new functionality. The situation you describe is exactly parallel to using UFCS (with regular functions) like this: //File: mystruct.d module mystruct; struct MyStruct { int _value; } //File: incr1.d module incr1; import mystruct; void incr(ref MyStruct ms) { ms._value += 1; } //File: incr2.d module incr2; import mystruct; void incr(ref MyStruct ms) { ms._value += 2; } //File: main.d module main; import std.stdio; import mystruct; static if (true) // change to false to print "2" import incr1; else import incr2; void main() { MyStruct ms; ms.incr(); writeln(ms._value); // prints "1" } Thirdly, I see no reason in allowing it - for what purpose does you proposal service for? The main reason to me is that it would make more sense. It'd seem more logical that way. I can't think of any use cases.
Re: Operator overloading through UFCS doesn't work
On Saturday, 13 October 2012 at 10:00:22 UTC, Tommi wrote: On Saturday, 13 October 2012 at 09:50:05 UTC, Jonathan M Davis wrote: It is most definitely _by design_ that you cannot overload operators except as member functions. I don't understand this design choice then. I don't see any problem in allowing UFCS operators. Because of the way UFCS works, it's guaranteed that there can't be any operator hijacking. I think implementing UFCS operator overloading is problematic. Firstly, you want to put this language addition too far. Secondly, compiler needs to know whether operator was overloaded or not. If it knows, it generates code to call "opSomething", if nor - it just doesn't generate anything. Now, imagine what would happen if you write in some module "free" function, supposed to hijack operator overloading method of class or struct in another module. If you compile them together, operator would be overloaded, if separately - nothing would happen. This means that operator overloading would depend on with what you compile your module - sometimes nothing would be overloaded, sometimes it would be with one function, sometimes with another. Thirdly, I see no reason in allowing it - for what purpose does you proposal service for?
Re: Operator overloading through UFCS doesn't work
On Saturday, 13 October 2012 at 09:50:05 UTC, Jonathan M Davis wrote: It is most definitely _by design_ that you cannot overload operators except as member functions. I don't understand this design choice then. I don't see any problem in allowing UFCS operators. Because of the way UFCS works, it's guaranteed that there can't be any operator hijacking.
Re: Operator overloading through UFCS doesn't work
On Saturday, October 13, 2012 11:41:07 Tommi wrote: > On Saturday, 13 October 2012 at 09:06:28 UTC, Jakob Ovrum wrote: > > Do note that this says *method* call. Your example doesn't use > > methods. Hence, the current state of operator overloading is > > consistent with TDPL. > > I don't agree with the last sentence. According to TDPL: > > 1) "whenever at least one participant in an operator expression > is of user-defined type, the compiler rewrites the expression > into a regular method call with a specific name" > --- > ++var; > gets rewritten to: > var.opUnary!"++"(); > > 2) "if a.fun(b, c, d) is seen but fun is not a member of a’s > type, D rewrites that as fun(a, b, c, d) and tries that as well" > > So, because opUnary is not a member of var, compiler should > rewrite that as: > .opUnary!"++"(var); Just because the overloaded operator is rewritten into a method call underneath the hood doesn't mean that the UFCS rewrite also applies. The transformation of an operator to a method or a UFCS call to a free function is done _once_. You don't get both. It is most definitely _by design_ that you cannot overload operators except as member functions. If TDPL says otherwise, it's either because you're misunderstanding what it's saying or because it's wrong. - Jonathan M Davis
Re: Operator overloading through UFCS doesn't work
On Saturday, 13 October 2012 at 09:06:28 UTC, Jakob Ovrum wrote: Do note that this says *method* call. Your example doesn't use methods. Hence, the current state of operator overloading is consistent with TDPL. I don't agree with the last sentence. According to TDPL: 1) "whenever at least one participant in an operator expression is of user-defined type, the compiler rewrites the expression into a regular method call with a specific name" --- ++var; gets rewritten to: var.opUnary!"++"(); 2) "if a.fun(b, c, d) is seen but fun is not a member of a’s type, D rewrites that as fun(a, b, c, d) and tries that as well" So, because opUnary is not a member of var, compiler should rewrite that as: .opUnary!"++"(var);
Re: Operator overloading through UFCS doesn't work
On Saturday, October 13, 2012 11:06:27 Jakob Ovrum wrote: > On Saturday, 13 October 2012 at 08:36:19 UTC, Tommi wrote: > > Quote from TDPL: "D’s approach to operator overloading is > > simple: whenever at least one participant in an operator > > expression is of user-defined type, the compiler rewrites the > > expression into a regular method call with a specific name. > > Then the regular language rules apply." > > Do note that this says *method* call. Your example doesn't use > methods. Hence, the current state of operator overloading is > consistent with TDPL. Yes. It is most definitely illegal to overload any operators as free functions. They're _always_ member variables of the type that they operate on. - Jonathan M Davis
Re: Operator overloading through UFCS doesn't work
On Saturday, 13 October 2012 at 08:36:19 UTC, Tommi wrote: Quote from TDPL: "D’s approach to operator overloading is simple: whenever at least one participant in an operator expression is of user-defined type, the compiler rewrites the expression into a regular method call with a specific name. Then the regular language rules apply." Do note that this says *method* call. Your example doesn't use methods. Hence, the current state of operator overloading is consistent with TDPL.