www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Operator overloading through UFCS doesn't work

reply "Tommi" <tommitissari hotmail.com> writes:
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."

According to the above, I think the following code should work:

struct MyStruct
{
     int _value;
}

ref MyStruct opUnary(string op : "++")(ref MyStruct ms)
{
     ++ms._value;
     return ms;
}

MyStruct opBinary(string op : "+")(MyStruct ms, int value)
{
     return MyStruct(ms._value + value);
}

void main()
{
     MyStruct ms;

     ms.opUnary!"++"();                 // #1: OK
     MyStruct ms2 = ms.opBinary!"+"(1); // #2: OK

     ++ms;                  // #3
     MyStruct ms3 = ms + 1; // #4
}

#3: Error: 'ms += 1' is not a scalar, it is a MyStruct

#4: Error: incompatible types for ((ms) + (1)):
'MyStruct' and 'int'


I'd expect the lines tagged #3 and #4 to be rewritten by the 
compiler like so:
ms.opUnary!"++"();
MyStruct ms3 = ms.opBinary!"+"(1);

So, the inability to do operator overloading though UFCS must be 
a compiler bug, right?
Oct 13 2012
next sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
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.
Oct 13 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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!)
Oct 13 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
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.
Oct 17 2012
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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.)
Oct 17 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
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?
Oct 17 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
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=E2=80=99s 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 fu= nctions.=20 They're _always_ member variables of the type that they operate on. - Jonathan M Davis
Oct 13 2012
prev sibling next sibling parent "Tommi" <tommitissari hotmail.com> writes:
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);
Oct 13 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
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: =20 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!"++"(); =20 2) "if a.fun(b, c, d) is seen but fun is not a member of a=E2=80=99s 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=20= underneath the hood doesn't mean that the UFCS rewrite also applies. Th= e=20 transformation of an operator to a method or a UFCS call to a free func= tion is=20 done _once_. You don't get both. It is most definitely _by design_ that= you=20 cannot overload operators except as member functions. If TDPL says othe= rwise,=20 it's either because you're misunderstanding what it's saying or because= it's=20 wrong. - Jonathan M Davis
Oct 13 2012
prev sibling next sibling parent "Tommi" <tommitissari hotmail.com> writes:
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.
Oct 13 2012
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
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?
Oct 13 2012
prev sibling next sibling parent "Tommi" <tommitissari hotmail.com> writes:
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.
Oct 13 2012
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
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.
Oct 13 2012
prev sibling next sibling parent reply "Tommi" <tommitissari hotmail.com> writes:
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)
Oct 13 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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.
Oct 13 2012
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
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.
Oct 13 2012
prev sibling parent reply Artur Skawina <art.08.09 gmail.com> writes:
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
Oct 14 2012
parent reply Artur Skawina <art.08.09 gmail.com> writes:
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
Oct 15 2012
next sibling parent reply Artur Skawina <art.08.09 gmail.com> writes:
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
Oct 15 2012
next sibling parent Artur Skawina <art.08.09 gmail.com> writes:
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
Oct 17 2012
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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.
Oct 17 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
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?
Oct 17 2012
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
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?
Oct 17 2012
prev sibling parent Artur Skawina <art.08.09 gmail.com> writes:
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
Oct 17 2012
prev sibling next sibling parent "Tommi" <tommitissari hotmail.com> writes:
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.
Oct 13 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
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<int>::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 different code which the compiler then compiles, 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. 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. - Jonathan M Davis
Oct 13 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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
Oct 13 2012
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
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.
Oct 13 2012
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
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.
Oct 13 2012
prev sibling next sibling parent "Tommi" <tommitissari hotmail.com> writes:
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.
Oct 14 2012
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
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.
Oct 14 2012
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
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.
Oct 14 2012
prev sibling next sibling parent "Tommi" <tommitissari hotmail.com> writes:
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).
Oct 14 2012
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
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.
Oct 15 2012
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
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.
Oct 15 2012
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
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.
Oct 15 2012
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
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.
Oct 16 2012
prev sibling next sibling parent "Tommi" <tommitissari hotmail.com> writes:
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.
Oct 16 2012
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
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.
Oct 16 2012
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
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.
 ...
 <skipped>
 artur

I don't see how this addresses the issue I try to put attention to. The discussion it turning into repetition of views.
Oct 17 2012
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
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.
Oct 17 2012
prev sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
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.
Oct 17 2012