October 14, 2012
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.
October 14, 2012
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
October 14, 2012
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).
October 15, 2012
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.
October 15, 2012
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:
<snip>

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.
October 15, 2012
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
October 15, 2012
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.
October 16, 2012
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
October 16, 2012
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.
October 16, 2012
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.