digitalmars.D - D1 operator overloads have been deprecated.
- uranuz (14/14) Jul 11 2019 In change log of nightly builds I see that:
- Timon Gehr (18/32) Jul 11 2019 import std.stdio;
- Nathan S. (3/4) Jul 11 2019 Nice workaround. That shouldn't be necessary though. Something so
- Bert (21/55) Jul 12 2019 um, the OP said "works with virtual functions/inheritance". When
- Timon Gehr (2/27) Jul 12 2019 Please do enlighten us how you would make that work with D1 operators.
- Bert (43/71) Jul 13 2019 I said nothing about getting it to work. It works
- FeepingCreature (19/22) Jul 12 2019 You're misunderstanding.
- Bert (39/62) Jul 13 2019 This works but now requires modifying the entire oop structure to
- Alexandru Ermicioi (5/8) Jul 15 2019 It should be easy to fix by implementing an interface with D2
- Alexandru Ermicioi (5/13) Jul 15 2019 And an even better alternative would be to use mixin templates
- Daniel N (21/28) Jul 15 2019 Just forward to the legacy D1 name.
- uranuz (10/10) Oct 06 2019 Hello! I think that this descision to deprecated operators was
- uranuz (6/6) Oct 06 2019 Now user of language must actually have two sets of functions
- Gregor =?UTF-8?B?TcO8Y2ts?= (7/13) Oct 06 2019 You only need this workaround if you habe a class hierarchy where
- uranuz (13/26) Oct 06 2019 Personally, I have several places in my code where I need to add
- Gregor =?UTF-8?B?TcO8Y2ts?= (3/16) Oct 06 2019 What is your use case for an in operator that changes in derived
- Jonathan M Davis (18/21) Oct 06 2019 It was the plan to remove the D1 operator overloading functions when the...
- uranuz (6/11) Oct 07 2019 Thanks. Understood. The problemme is just that this solution
- aliak (10/76) Jul 15 2019 I'm curious where you get the "whole shit load of code is going
- Basile B. (7/13) Jul 11 2019 It rather looks like a big whoopsie. Nobody has thought to this
- Adam D. Ruppe (4/6) Jul 11 2019 I remember it coming up, maybe not formally, but the solution of
- H. S. Teoh (10/17) Jul 11 2019 If it's a 1-line fix, I doubt it would stop W&A from going ahead anyway,
- Jonathan M Davis (11/24) Jul 11 2019 It was decided years ago that the old, non-templated overloaded operator...
In change log of nightly builds I see that: `D1 operator overloads have been deprecated` The major concern about it is that D2 style operators are template functions. And template functions cannot be virtual. Does it mean that we shall not be able to decalare operators in classes and interfaces that could be overloaded in terms of OOP. What is the proposed solution to this problemme? Currently the only way I see is to declare `final` opOpAssign(RHS, string op)(RHS rhs) (for instance). And then dispatch them to `regular` virtual functions. But it shall look the same as these operator that we are trying to deprecate, but with extra `wrapper`. What is the profit of deprecating these D1? Or is it meant that virtual operators in generally a bad practice?! But I don't understand - why?
Jul 11 2019
On 11.07.19 19:58, uranuz wrote:In change log of nightly builds I see that: `D1 operator overloads have been deprecated` The major concern about it is that D2 style operators are template functions. And template functions cannot be virtual. Does it mean that we shall not be able to decalare operators in classes and interfaces that could be overloaded in terms of OOP. What is the proposed solution to this problemme? Currently the only way I see is to declare `final` opOpAssign(RHS, string op)(RHS rhs) (for instance). And then dispatch them to `regular` virtual functions. But it shall look the same as these operator that we are trying to deprecate, but with extra `wrapper`.import std.stdio; class C{ 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(){ auto a=new C(1),b=new C(2); writeln((a+b).x); } You can probably even write a mixin template that automatically upgrades your class from D1 style operators to D2 style.What is the profit of deprecating these D1? ...When designing a language from scratch, probably you wouldn't add two ways to declare operators, especially if one of them subsumes the other.
Jul 11 2019
On Thursday, 11 July 2019 at 19:07:28 UTC, Timon Gehr wrote:alias opBinary(string op:"+")=opAddImpl;Nice workaround. That shouldn't be necessary though. Something so fundamental shouldn't require 'tricks'.
Jul 11 2019
On Thursday, 11 July 2019 at 19:07:28 UTC, Timon Gehr wrote:On 11.07.19 19:58, uranuz 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.In change log of nightly builds I see that: `D1 operator overloads have been deprecated` The major concern about it is that D2 style operators are template functions. And template functions cannot be virtual. Does it mean that we shall not be able to decalare operators in classes and interfaces that could be overloaded in terms of OOP. What is the proposed solution to this problemme? Currently the only way I see is to declare `final` opOpAssign(RHS, string op)(RHS rhs) (for instance). And then dispatch them to `regular` virtual functions. But it shall look the same as these operator that we are trying to deprecate, but with extra `wrapper`.import std.stdio; class C{ 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(){ auto a=new C(1),b=new C(2); writeln((a+b).x); } You can probably even write a mixin template that automatically upgrades your class from D1 style operators to D2 style.What is the profit of deprecating these D1? ...When designing a language from scratch, probably you wouldn't add two ways to declare operators, especially if one of them subsumes the other.
Jul 12 2019
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.
Jul 12 2019
On Friday, 12 July 2019 at 15:07:11 UTC, Timon Gehr wrote:On 12.07.19 15:42, Bert wrote: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.)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.
Jul 13 2019
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); }
Jul 12 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: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.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); }
Jul 13 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.
Jul 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: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!().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.
Jul 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: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); }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.
Jul 15 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..
Oct 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.
Oct 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.
Oct 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: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...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.
Oct 06 2019
On Sunday, 6 October 2019 at 12:28:57 UTC, uranuz wrote:On Sunday, 6 October 2019 at 10:39:04 UTC, Gregor Mückl wrote:What is your use case for an in operator that changes in derived classes?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.
Oct 06 2019
On Sunday, October 6, 2019 6:28:57 AM MDT uranuz via Digitalmars-d wrote: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...It was the plan to remove the D1 operator overloading functions when the D2, templated operators were added to the language. The D1 operators have only been around this long, because no one got around to deprecating them. Having multiple ways to overload operators complicates the language and provides almost no benefit. The only thing that you can't do with the templated operators that you can do with the D1 operators is have them be virtual, which doesn't matter for the vast majority of D code, since most D code uses structs rather than classes. And for the code that does use classes and actually needs the operators to be virtual, it's trivial to just put the overloaded operator in the base class and then have protected, virtual functions that the derived classes override. It's what you'd do with the NVI (non-virtual inheritance) pattern anyway. So, code that uses classes and needs virtual, overloaded operators is going to be in the minority, and there's a clear and easy way to use virtual functions with the templated operators. As such, the extra complication of having the D1 operators in the language is not considered to be worth it. - Jonathan M Davis
Oct 06 2019
On Monday, 7 October 2019 at 03:23:27 UTC, Jonathan M Davis wrote: ...And for the code that does use classes and actually needs the operators to be virtual, it's trivial to just put the overloaded operator in the base class and then have protected, virtual functions that the derived classes override. It's what you'd do with the NVI (non-virtual inheritance) pattern anyway.Thanks. Understood. The problemme is just that this solution feels like some ugly hack (in Russian language we call it "костыль"). But seems that there is nothing to do with this already. No matter...
Oct 07 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: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.On Friday, 12 July 2019 at 13:42:35 UTC, Bert wrote: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.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); }
Jul 15 2019
On Thursday, 11 July 2019 at 17:58:50 UTC, uranuz wrote:In change log of nightly builds I see that: `D1 operator overloads have been deprecated` The major concern about it is that D2 style operators are template functions. And template functions cannot be virtual.Nice catch...Does it mean that we shall not be able to decalare operators in classes and interfaces that could be overloaded in terms of OOP.It rather looks like a big whoopsie. Nobody has thought to this case during the review stage[1]. I don't know if the change would have been accepted otherwise, even if they were not documented anymore. [1] https://github.com/dlang/dmd/pull/10130
Jul 11 2019
On Thursday, 11 July 2019 at 20:29:19 UTC, Basile B. wrote:It rather looks like a big whoopsie. Nobody has thought to this case during the review stage[1]I remember it coming up, maybe not formally, but the solution of just forwarding the final templates to a virtual implementation, mentioned above, has been around for a while.
Jul 11 2019
On Thu, Jul 11, 2019 at 08:35:13PM +0000, Adam D. Ruppe via Digitalmars-d wrote:On Thursday, 11 July 2019 at 20:29:19 UTC, Basile B. wrote:If it's a 1-line fix, I doubt it would stop W&A from going ahead anyway, since D1 operators have been deprecated for quite a while now. On a separate note, I think somebody mentioned a while ago that there are ways to make template functions virtual. If this is a really important issue, we should explore that instead of clinging on to old cruft that's supposed to have been gone years ago. T -- An elephant: A mouse built to government specifications. -- Robert HeinleinIt rather looks like a big whoopsie. Nobody has thought to this case during the review stage[1]I remember it coming up, maybe not formally, but the solution of just forwarding the final templates to a virtual implementation, mentioned above, has been around for a while.
Jul 11 2019
On Thursday, July 11, 2019 2:29:19 PM MDT Basile B. via Digitalmars-d wrote:On Thursday, 11 July 2019 at 17:58:50 UTC, uranuz wrote:It was decided years ago that the old, non-templated overloaded operators would be removed from the language, and it was well-known that if you then wanted virtual operator overloading, you'd need to forward to a protected, virtual function. Most user-defined types in D are structs anyway, and it's a simple workaround when you need virtual operator overloading with classes. I actually thought that the old operators had been fully removed quite some time ago, but I guess that it's on the list of stuff where we clearly decided that it was going away, but no one got around to actually deprecating it. - Jonathan M DavisIn change log of nightly builds I see that: `D1 operator overloads have been deprecated` The major concern about it is that D2 style operators are template functions. And template functions cannot be virtual.Nice catch...Does it mean that we shall not be able to decalare operators in classes and interfaces that could be overloaded in terms of OOP.It rather looks like a big whoopsie. Nobody has thought to this case during the review stage[1]. I don't know if the change would have been accepted otherwise, even if they were not documented anymore. [1] https://github.com/dlang/dmd/pull/10130
Jul 11 2019