July 14, 2019
On Friday, 12 July 2019 at 15:07:11 UTC, Timon Gehr wrote:
> On 12.07.19 15:42, Bert wrote:
>> 
>> um, the OP said "works with virtual functions/inheritance". When are people going to learn that aliases are not virtual and have nothing to do with preserving inheritance structure?
>> 
>> 
>> import std.stdio;
>> 
>> class X
>> {
>> }
>> class C : X{
>>      int x;
>>      this(int x){ this.x=x; }
>>      C opAddImpl(C rhs){
>>          return new C(x+rhs.x);
>>      }
>>      alias opBinary(string op:"+")=opAddImpl;
>> }
>> void main(){
>>      X a=new C(1),b=new C(2);
>>      writeln((a+b).x);
>> }
>> 
>> fails.
>
> Please do enlighten us how you would make that work with D1 operators.

I said nothing about getting it to work. It works

import std.stdio;

abstract class X
{
    //abstract X opNeg(); // D1
    abstract X opNegImpl(); // D2
    alias opUnary(string op : "-") = opNegImpl; // D2
}

class C : X
{
    int x;
    this(int x) { this.x = x; }
    //override C opNeg(){ return new C(-this.x); } D1
    override C opNegImpl() { return new C(-x); }

}

void main()
{
    X a = new C(1), b = new C(2);
    writeln((cast(C)(-b)).x);
}

There is nothing wrong with D1 operators! Unless there is a deeper problem besides just freeing names, what is being done is a whole lot of old D code will be broke simply to free opXXX id's... This type of thing is called *making a small problem much bigger*.


Maybe the better way would be to convert the D1 op names to

opD1XXX

then at least a simple search and replace can solve 99% of the problems... of course, there is no real problem because someone will come along and complain that the id name space is polluted with opD1XXX's and must be removed... at some point someone has to stand up and say "It's not an issue but breaking hundreds of thousands of lines of library code is!".

If you think opD1XXX is too common, then _op_D1_OLD_OPERATOR_NAME__XXX...

It's irrelevant, but deprecating something just for the hell of it is wrong and that is what *seems* to be done here(I'm going off what J.D. said about there being two ways to do the same way, the old D1 way with fixed opXXX and the new way with opXXX templates, which, are in fact essentially identical semantically with a slight syntactical difference... sorta like {} VS BEGIN and END.)




July 14, 2019
On Friday, 12 July 2019 at 15:21:10 UTC, FeepingCreature wrote:
> On Friday, 12 July 2019 at 13:42:35 UTC, Bert wrote:
>> um, the OP said "works with virtual functions/inheritance". When are people going to learn that aliases are not virtual and have nothing to do with preserving inheritance structure?
>>
>
> You're misunderstanding.
>
> import std.stdio;
>
> abstract class X
> {
>     abstract X opAddImpl(X rhs);
>     alias opBinary(string op:"+") = opAddImpl;
> }
>
> class C : X{
>     int x;
>     this(int x){ this.x=x; }
>     override C opAddImpl(X rhs){
>         return new C(x+(cast(C) rhs).x);
>     }
> }
> void main(){
>     X a=new C(1),b=new C(2);
>     writeln((cast(C) (a+b)).x);
> }


This works but now requires modifying the entire oop structure to conform. This is now how oop is suppose to work or good design decisions. We are talking about a pre-existing oop design that might be 100's of classes in size.

It's all fine and dandy when you are designing something from scratch but since D1 essentially does this anyways, what's the point of depreciating it then requiring it?

https://digitalmars.com/d/1.0/operatoroverloading.html

import std.stdio;

abstract class X
{
    //abstract X opNeg(); // D1
    abstract X opNegImpl(); // D2
    alias opUnary(string op : "-") = opNegImpl; // D2
}

class C : X
{
    int x;
    this(int x) { this.x = x; }
    //override C opNeg(){ return new C(-this.x); } D1
    override C opNegImpl() { return new C(-x); }

}

void main()
{
    X a = new C(1), b = new C(2);
    writeln((cast(C)(-b)).x);
}



So, all that has been achieved is allowing one to use their own base function names. A whole shit load of code is going to be broke SIMPLY to remove the default opXXX names... names that would rarely be used in any code for any other purpose anyways(I've never in my live used an id starting with op that was not meant to be used as an operator).

Absolutely nothing is achieved if the only issue is "there are "two" ways of doing it" as J.D. says.


In the new way we have to have an alias redirector and the function rather than just the function. All we end up getting is freeing a member id name and a whole lot of broken code.
July 15, 2019
On Sunday, 14 July 2019 at 05:27:28 UTC, Bert wrote:
> In the new way we have to have an alias redirector and the function rather than just the function. All we end up getting is freeing a member id name and a whole lot of broken code.


It should be easy to fix by implementing an interface with D2 templated operators that forward to D1. The downside would be that you'll need to add the interface to each class/interface with existing D1 operators.
July 15, 2019
On Monday, 15 July 2019 at 08:00:26 UTC, Alexandru Ermicioi wrote:
> On Sunday, 14 July 2019 at 05:27:28 UTC, Bert wrote:
>> In the new way we have to have an alias redirector and the function rather than just the function. All we end up getting is freeing a member id name and a whole lot of broken code.
>
>
> It should be easy to fix by implementing an interface with D2 templated operators that forward to D1. The downside would be that you'll need to add the interface to each class/interface with existing D1 operators.

And an even better alternative would be to use mixin templates that introspect aggregate entity for d1 operators, and define d2 forwarders. Then fixing woukd be just one one for each aggregate: mixin D1OperatorFix!().
July 15, 2019
On Monday, 15 July 2019 at 08:07:37 UTC, Alexandru Ermicioi wrote:
> On Monday, 15 July 2019 at 08:00:26 UTC, Alexandru Ermicioi wrote:
>> On Sunday, 14 July 2019 at 05:27:28 UTC, Bert wrote:
>>> In the new way we have to have an alias redirector and the function rather than just the function. All we end up getting is freeing a member id name and a whole lot of broken code.
>>

Just forward to the legacy D1 name.
*) Only have to change base class
*) Backwards and forwards compatible

import std.stdio;

abstract class X
{
    abstract X opNeg(); // D1
    alias opUnary(string op : "-") = opNeg; // D2
}

class C : X
{
    int x;
    this(int x) { this.x = x; }
    override C opNeg() { return new C(-x); }

}

void main()
{
    X a = new C(1), b = new C(2);
    writeln((cast(C)(-b)).x);
}
July 15, 2019
On Sunday, 14 July 2019 at 05:27:28 UTC, Bert wrote:
> On Friday, 12 July 2019 at 15:21:10 UTC, FeepingCreature wrote:
>> On Friday, 12 July 2019 at 13:42:35 UTC, Bert wrote:
>>> um, the OP said "works with virtual functions/inheritance". When are people going to learn that aliases are not virtual and have nothing to do with preserving inheritance structure?
>>>
>>
>> You're misunderstanding.
>>
>> import std.stdio;
>>
>> abstract class X
>> {
>>     abstract X opAddImpl(X rhs);
>>     alias opBinary(string op:"+") = opAddImpl;
>> }
>>
>> class C : X{
>>     int x;
>>     this(int x){ this.x=x; }
>>     override C opAddImpl(X rhs){
>>         return new C(x+(cast(C) rhs).x);
>>     }
>> }
>> void main(){
>>     X a=new C(1),b=new C(2);
>>     writeln((cast(C) (a+b)).x);
>> }
>
>
> This works but now requires modifying the entire oop structure to conform. This is now how oop is suppose to work or good design decisions. We are talking about a pre-existing oop design that might be 100's of classes in size.
>
> It's all fine and dandy when you are designing something from scratch but since D1 essentially does this anyways, what's the point of depreciating it then requiring it?
>
> https://digitalmars.com/d/1.0/operatoroverloading.html
>
> import std.stdio;
>
> abstract class X
> {
>     //abstract X opNeg(); // D1
>     abstract X opNegImpl(); // D2
>     alias opUnary(string op : "-") = opNegImpl; // D2
> }
>
> class C : X
> {
>     int x;
>     this(int x) { this.x = x; }
>     //override C opNeg(){ return new C(-this.x); } D1
>     override C opNegImpl() { return new C(-x); }
>
> }
>
> void main()
> {
>     X a = new C(1), b = new C(2);
>     writeln((cast(C)(-b)).x);
> }
>
>
>
> So, all that has been achieved is allowing one to use their own base function names. A whole shit load of code is going to be broke SIMPLY to remove the default opXXX names... names that would rarely be used in any code for any other purpose anyways(I've never in my live used an id starting with op that was not meant to be used as an operator).
>
> Absolutely nothing is achieved if the only issue is "there are "two" ways of doing it" as J.D. says.
>
>
> In the new way we have to have an alias redirector and the function rather than just the function. All we end up getting is freeing a member id name and a whole lot of broken code.

I'm curious where you get the "whole shit load of code is going to be broken" part?

And what do you suggest, that D keep supporting all its legacy? Or is it "just this one"? You can think that it's just one little feature but it's not - it's a lot of small features that are duplicated, that have to be maintained, that get bug reports because they are supposed to be working, and that actively hinder the development and solidifying of more useful features and features that supercede older features.  Bloat is bad.
October 06, 2019
Hello! I think that this descision to deprecated operators was made without analysis about consequences that we will have for classes that use this operator. I think there should be a DIP with analysis of the problemme. Because in this discussion we had a talk about different workarounds to solve the problemme that we actually created by ourselves by accepting this "improvement" without proper analysis.
Benefits of this descision are completely unclear for user of the language.
It's very very strange to introduce breaking changes that way..
October 06, 2019
Now user of language must actually have two sets of functions with proposed workaround with classes. The final template function and the old-style D1 (yes we can rename it and so on, but this doesn't change so much). We should have two entities instead of one that we had. So this is very "contradictive" improvement.
October 06, 2019
On Sunday, 6 October 2019 at 08:34:40 UTC, uranuz wrote:
> Now user of language must actually have two sets of functions with proposed workaround with classes. The final template function and the old-style D1 (yes we can rename it and so on, but this doesn't change so much). We should have two entities instead of one that we had. So this is very "contradictive" improvement.

You only need this workaround if you habe a class hierarchy where overloaded operators are
overridden in subclasses. This is a very narrow set of cases. Otherwise, implementing only the D2 way is sufficient. How often do you actually need to override operator behaviour in subclasses? I'm genuinely curious about use cases.
October 06, 2019
On Sunday, 6 October 2019 at 10:39:04 UTC, Gregor Mückl wrote:
> On Sunday, 6 October 2019 at 08:34:40 UTC, uranuz wrote:
>> Now user of language must actually have two sets of functions with proposed workaround with classes. The final template function and the old-style D1 (yes we can rename it and so on, but this doesn't change so much). We should have two entities instead of one that we had. So this is very "contradictive" improvement.
>
> You only need this workaround if you habe a class hierarchy where overloaded operators are
> overridden in subclasses. This is a very narrow set of cases. Otherwise, implementing only the D2 way is sufficient. How often do you actually need to override operator behaviour in subclasses? I'm genuinely curious about use cases.

Personally, I have several places in my code where I need to add `in` into basic interface and implement it in derived classes. It is not some "weird", "narrow" set of cases, I believe.
For instance, I have different implementations of `in` in all of these classes. The main problemme for me is that you need to change the basic interface, that is used by all the classes. It is not very good, especially if anybody still cannot explain what benefits it gives me. And also you (maybe) need to fix all of the derived implementations...
Every time I do something I want to know: what for? Especially, if it touches some basic aspects. But still I haven't got any answer...