Re: Operator "+=" overloading for class?
On Wednesday, 20 December 2023 at 02:56:24 UTC, Steven Schveighoffer wrote: Instead you are trying to reassign the `this` reference, which is a local (and also forbidden). Think about it a second, your `this + rhs` is going to allocate a *new* object. Then if the assignment to the local `this` parameter succeeded, what happens outside the member function? The true reference that is calling this will not be updated! The only way to do this I can see is to reimplement for op=, or maybe perform the operation and swap the guts out. -Steve Thank you for such a detailed explanation! That does make sense.
Re: Operator "+=" overloading for class?
On Monday, 18 December 2023 at 14:38:14 UTC, Ki Rill wrote: your code just return result value, but it should not return but save result to "this" see example at https://dlang.org/spec/operatoroverloading.html#index_op_assignment I get an error, but don't understand why. ```d auto opOpAssign(string op)(Value rhs) { this = this + rhs; return this; } // Error: `this` is not an lvalue and cannot be modified ``` Assigning an object is like copying a pointer. You may think you can try overloading the assignment, but it is [forbidden](https://dlang.org/spec/operatoroverloading.html#assignment): ``` However for class types, identity assignment is not allowed. All class types have reference semantics, so identity assignment by default rebinds the left-hand-side to the argument at the right, and this is not overridable. ``` But you aren't trying to do this. Instead you are trying to reassign the `this` reference, which is a local (and also forbidden). Think about it a second, your `this + rhs` is going to allocate a *new* object. Then if the assignment to the local `this` parameter succeeded, what happens outside the member function? The true reference that is calling this will not be updated! The only way to do this I can see is to reimplement for op=, or maybe perform the operation and swap the guts out. -Steve
Re: Operator "+=" overloading for class?
On Sunday, 17 December 2023 at 04:13:20 UTC, Ki Rill wrote: I am trying to overload `opOpAssign` for my class. The code compiles, but it does not seem to be working. ```d // binary operations have already been implemented for Value // i need +=, -=, *=, /= auto opOpAssign(string op)(Value rhs) { mixin("return this" ~ op ~ "rhs;"); } auto opOpAssign(string op)(in ElementType rhs) { mixin("return this" ~ op ~ "rhs;"); } ``` What am I missing here? Full project code can be found [here](https://github.com/rillki/tiny-grad). Perhaps: ```d auto opOpAssign(string op)(Value other) { mixin("this.v " ~ op ~ "= other.v;"); return this; } ```
Re: Operator "+=" overloading for class?
On Monday, 18 December 2023 at 04:49:53 UTC, novice2 wrote: On Monday, 18 December 2023 at 03:39:16 UTC, Ki Rill wrote: On Sunday, 17 December 2023 at 07:05:12 UTC, Adam D. Ruppe wrote: On Sunday, 17 December 2023 at 04:13:20 UTC, Ki Rill wrote: [...] check what `op` is. pretty sure it is "+" not "+=" so your element isnt' saved anywhere. also a bit iffy there isn't a member here to work on Yes, op is '+'. What do you mean by it isn't saved anywhere? your code just return result value, but it should not return but save result to "this" see example at https://dlang.org/spec/operatoroverloading.html#index_op_assignment I get an error, but don't understand why. ```d auto opOpAssign(string op)(Value rhs) { this = this + rhs; return this; } // Error: `this` is not an lvalue and cannot be modified ```
Re: Operator "+=" overloading for class?
On Monday, 18 December 2023 at 03:39:16 UTC, Ki Rill wrote: On Sunday, 17 December 2023 at 07:05:12 UTC, Adam D. Ruppe wrote: On Sunday, 17 December 2023 at 04:13:20 UTC, Ki Rill wrote: [...] check what `op` is. pretty sure it is "+" not "+=" so your element isnt' saved anywhere. also a bit iffy there isn't a member here to work on Yes, op is '+'. What do you mean by it isn't saved anywhere? your code just return result value, but it should not return but save result to "this" see example at https://dlang.org/spec/operatoroverloading.html#index_op_assignment
Re: Operator "+=" overloading for class?
On Sunday, 17 December 2023 at 07:05:12 UTC, Adam D. Ruppe wrote: On Sunday, 17 December 2023 at 04:13:20 UTC, Ki Rill wrote: [...] check what `op` is. pretty sure it is "+" not "+=" so your element isnt' saved anywhere. also a bit iffy there isn't a member here to work on Yes, op is '+'. What do you mean by it isn't saved anywhere? I tried reassigning to `this` and returning it, but it fails. I want to achieve this, but with '+=': ```d auto g = value(0); g = g + value(3); // here it should just create a new instance 'g + value(3)' and save it into 'g' g += value(3); ``` Do you have an advice on how to achieve it?
Re: Operator "+=" overloading for class?
On Sunday, 17 December 2023 at 04:13:20 UTC, Ki Rill wrote: auto opOpAssign(string op)(in ElementType rhs) { mixin("return this" ~ op ~ "rhs;"); } ``` check what `op` is. pretty sure it is "+" not "+=" so your element isnt' saved anywhere. also a bit iffy there isn't a member here to work on
Re: Operator "+=" overloading for class?
On Sunday, 17 December 2023 at 04:15:02 UTC, Ki Rill wrote: On Sunday, 17 December 2023 at 04:13:20 UTC, Ki Rill wrote: I am trying to overload `opOpAssign` for my class. The code [...] I forgot to mention, it is relevant to [`Value`](https://github.com/rillki/tiny-grad/blob/main/source/rk/tgrad/core/value.d) class only. This unittest fails: ```d // test opOpAssign auto g = value(0); g += value(3); assert(g.data == 3); ```
Re: Operator "+=" overloading for class?
On Sunday, 17 December 2023 at 04:13:20 UTC, Ki Rill wrote: I am trying to overload `opOpAssign` for my class. The code [...] I forgot to mention, it is relevant to [`Value`](https://github.com/rillki/tiny-grad/blob/main/source/rk/tgrad/core/value.d) class only.
Operator "+=" overloading for class?
I am trying to overload `opOpAssign` for my class. The code compiles, but it does not seem to be working. ```d // binary operations have already been implemented for Value // i need +=, -=, *=, /= auto opOpAssign(string op)(Value rhs) { mixin("return this" ~ op ~ "rhs;"); } auto opOpAssign(string op)(in ElementType rhs) { mixin("return this" ~ op ~ "rhs;"); } ``` What am I missing here? Full project code can be found [here](https://github.com/rillki/tiny-grad).
Re: Operator Overloading for Enum
On Monday, 8 February 2021 at 15:56:24 UTC, Michael Brown wrote: Hi all, Is it possible to operator overload on enums? I'd like to do a opCmp() Kind regards, Mike You can create custom struct type with opCmp() and create enum with that type. Example: https://run.dlang.io/is/m7DN66
Operator Overloading for Enum
Hi all, Is it possible to operator overload on enums? I'd like to do a opCmp() Kind regards, Mike
Re: Operator Overloading for Enum
On Monday, 8 February 2021 at 15:56:24 UTC, Michael Brown wrote: Hi all, Is it possible to operator overload on enums? I'd like to do a opCmp() Kind regards, Mike No, it isn't. Only structs and classes can have overloaded operators.
Re: opCast / operator overloading with additional template arguments
On 1/10/21 7:27 PM, Paul wrote: > On Monday, 11 January 2021 at 02:37:24 UTC, Ali Çehreli wrote: >> >> T opCast(T)() const if (is(T : Vec!(size, S2), S2)) { > >> The is expression can be so complicated that I used a different >> approach below. > >> if (isInstanceOfVec!T && >> T.init.content.length == size) { >> // ADDED: >> alias S2 = typeof(T.init.content[0]); > > Is it ok to use .init even though we don't need an instantiated value? Yes, .init useful in many templates. However, you don't actually use that object because typeof does not evaluate the expression, just produces the type of it. Related, the content[0] expression might be seen as invalid code because the array may not have any elements at all but again, there is no access to element 0. There are other ways e.g. import std.range; alias S2 = ElementType!(typeof(T.init.content)); Ali
Re: opCast / operator overloading with additional template arguments
On Monday, 11 January 2021 at 03:40:41 UTC, Paul wrote: On Monday, 11 January 2021 at 00:48:49 UTC, Steven Schveighoffer wrote: I would think though, that this should work: T opCast(T : Vec!(vecsize, S), S)() Oh wouw, this seems to work perfectly! Awesome thanks ^^ Any Idea why T opCast(T, S)() const if (is(T : Vec!(grootte, S))) { yields the error template instance opCast!(Vec!(2, double)) does not match template declaration opCast(T, S)() while your suggestion does not? It seems to me it should match equally well. The compiler does not look at template constraints until after it figures out what all the template arguments are. So, in your version, it sees T opCast(T, S) // ... opCast!(Vec!(2, double)) ...and is able to deduce that `T = Vec!(2, double)`, but doesn't have any way to figure out what `S` is, so it gives up.
Re: opCast / operator overloading with additional template arguments
On Monday, 11 January 2021 at 00:48:49 UTC, Steven Schveighoffer wrote: I would think though, that this should work: T opCast(T : Vec!(vecsize, S), S)() Oh wouw, this seems to work perfectly! Awesome thanks ^^ Any Idea why T opCast(T, S)() const if (is(T : Vec!(grootte, S))) { yields the error template instance opCast!(Vec!(2, double)) does not match template declaration opCast(T, S)() while your suggestion does not? It seems to me it should match equally well. Also I had no clue types inferred in constraints were inaccessibly, I'll try to keep that in mind, though I wonder, is there is any specific reason for that? Then again since your example works inferred values shouldnt be necessary in constraints anyway. (On that note, is there per chance something like the underscore '_' as in python? In some cases I don't care for all the template arguments inside an is expression (with the (a:b, c) version))
Re: opCast / operator overloading with additional template arguments
On Monday, 11 January 2021 at 02:37:24 UTC, Ali Çehreli wrote: >> T opCast(T)() const if (is(T : Vec!(size, S2), S2)) { The is expression can be so complicated that I used a different approach below. if (isInstanceOfVec!T && T.init.content.length == size) { // ADDED: alias S2 = typeof(T.init.content[0]); Is it ok to use .init even though we don't need an instantiated value?
Re: opCast / operator overloading with additional template arguments
On 1/10/21 6:37 PM, Ali Çehreli wrote: >// OBSERVATION: Should the cast below be S? >converted.content[i] = cast(S2) content[i]; I take that back. Yes, it should be S2. (I've been off lately. :) ) Ali
Re: opCast / operator overloading with additional template arguments
On 1/10/21 5:09 PM, Paul wrote: > I'll paste more of my file, I hope that's ok. Not only ok but much appreciated. :) >> T opCast(T)() const if (is(T : Vec!(size, S2), S2)) { The is expression can be so complicated that I used a different approach below. I left notes in capital letters below. Especially cast(S2) looks wrong to me: import std; version (HoekjeD_Double) { private alias standard_accuracy = double; } else { private alias standard_accuracy = float; } struct Vec(int size, S = standard_accuracy) { // You could add this instead of getting the type from // 'content' as in how S2 is aliased in opCast below: // // alias AccuracyType = S; union { S[size] content; static if (size >= 1) { struct { S x; static if (size >= 2) { S y; static if (size >= 3) { S z; static if (size >= 4) S w; } } } } } T opCast(T)() const // CHANGED: if (isInstanceOfVec!T && T.init.content.length == size) { // ADDED: alias S2 = typeof(T.init.content[0]); T converted; static foreach (i; 0 .. size) // OBSERVATION: Should the cast below be S? converted.content[i] = cast(S2) content[i]; return converted; } } enum isInstanceOfVec(T) = isInstanceOf!(Vec, T); void main() { auto a = Vec!(42, float)(); auto b = a.to!(Vec!(42, double)); } Ali
Re: opCast / operator overloading with additional template arguments
On Monday, 11 January 2021 at 00:25:36 UTC, Ali Çehreli wrote: You don't show complete code; so, I hope I came up with something that reflects your case. Thank you, sadly S (S2 now) is not any specific type, sorry I'll paste more of my file, I hope that's ok. (Sidenote, I'm not sure it's the most elegant approach to have a templated union like this, and I left out some unnecessary stuff like 'opBinary') version (HoekjeD_Double) { private alias standard_accuracy = double; } else { private alias standard_accuracy = float; } struct Vec(int size, S = standard_accuracy) { union { S[size] content; static if (size >= 1) { struct { S x; static if (size >= 2) { S y; static if (size >= 3) { S z; static if (size >= 4) S w; } } } } } T opCast(T)() const if (is(T : Vec!(size, S2), S2)) { T converted; static foreach (i; 0 .. size) converted.content[i] = cast(S2) content[i]; return converted; } }
Re: opCast / operator overloading with additional template arguments
On 1/10/21 7:09 PM, Paul wrote: Is there a way to have additional template arguments in operator overloads? The problem I'm having is mostly caused by casting a templated struct to other templated structs. I had the following code; T opCast(T)() const if (is(T : Vec!(vecsize, S), S)) { T converted; static foreach (i; 0 .. vecsize) converted.content[i] = cast(S) content[i]; return converted; } When I try to use this, I get the error 'undefined identifier S'. Alternatively using: T opCast(T, S)() . . . causes the error 'template instance opCast!(Vec!(2, double)) does not match template declaration opCast(T, S)()' I found using 'alias S = typeof(content[0])' works as a kind of ducttape alternative, however this seems like a codesmell to me, and I fear it won't scale well either. Am I missing a simple solution? And why is there no automatic argument deduction in this scenario when compared to normal function calls? (if the above opcast is translated to 'foo.opCast!T`) It has nothing to do with operator overloads. types inferred inside template constraints are not accessible inside the function. The awkward solution is to use the same is expression inside the function: static if(is(T : Vec!(vecsize, S), S)) {} // now you can use S I would think though, that this should work: T opCast(T : Vec!(vecsize, S), S)() But I don't have your full code to test. -Steve
Re: opCast / operator overloading with additional template arguments
On 1/10/21 4:09 PM, Paul wrote: > Is there a way to have additional template arguments in operator overloads? I haven't tried that but the following method seems to work for you. You don't show complete code; so, I hope I came up with something that reflects your case. import std; struct S(T) { auto opCast(U)() const if (isInstanceOfS!U) { writefln!"Converting from %s to %s"(S.stringof, U.stringof); return U.init; } } enum isInstanceOfS(T) = isInstanceOf!(S, T); void main() { auto a = S!int(); auto b = cast(S!double)a; // Works auto c = a.to!(S!double); // This works too } I needed the isInstanceOfS wrapper over std.traits.isInstanceOf because I could not use isInstanceOf inside the definition of S because the symbol 'S' does not mean "the template S", but the specific instantiation of it e.g. S!int. Outside, S means "the template S". Ali
opCast / operator overloading with additional template arguments
Is there a way to have additional template arguments in operator overloads? The problem I'm having is mostly caused by casting a templated struct to other templated structs. I had the following code; T opCast(T)() const if (is(T : Vec!(vecsize, S), S)) { T converted; static foreach (i; 0 .. vecsize) converted.content[i] = cast(S) content[i]; return converted; } When I try to use this, I get the error 'undefined identifier S'. Alternatively using: T opCast(T, S)() . . . causes the error 'template instance opCast!(Vec!(2, double)) does not match template declaration opCast(T, S)()' I found using 'alias S = typeof(content[0])' works as a kind of ducttape alternative, however this seems like a codesmell to me, and I fear it won't scale well either. Am I missing a simple solution? And why is there no automatic argument deduction in this scenario when compared to normal function calls? (if the above opcast is translated to 'foo.opCast!T`)
Re: Using multiple mixin templates to implement operator overloading
On Saturday, 12 December 2020 at 20:25:48 UTC, Adam D. Ruppe wrote: On Saturday, 12 December 2020 at 18:14:31 UTC, Paul Backus wrote: IMO this is one of the stupider design decisions in D, but it's unlikely it will ever be fixed. It is useful in several other contexts though, including user overriding and private data stores for the mixin. Sure, but you can always opt in to that behavior by giving the mixin a name. The fact that you can't opt out of it even if you want to is the issue. It's essentially the same flaw Andrei criticized C++'s `if constexpr` for [1]. [1] https://www.youtube.com/watch?v=tcyb1lpEHm0&t=45m20s
Re: Using multiple mixin templates to implement operator overloading
On Saturday, 12 December 2020 at 20:26:00 UTC, Dennis wrote: If issue 19365 got fixed eeek I thought that was fixed but apparently not :( so yeah alias won't work for operator overloads. Does work for other functions so good technique to know but not here. So for op prolly go with the string mixin
Re: Using multiple mixin templates to implement operator overloading
On Saturday, 12 December 2020 at 18:14:31 UTC, Paul Backus wrote: IMO this is one of the stupider design decisions in D, but it's unlikely it will ever be fixed. It is useful in several other contexts though, including user overriding and private data stores for the mixin. The easiest workaround is to use string mixins instead, which work the way you'd expect them to. But yeah. The other alternative is to alias them together: class A { mixin Whatever a; // need to give it a name here to reference later mixin Whatever b; // explicitly merge overloads here alias opBinary = a.opBinary; alias opBinary = b.opBinary; } Or you can do a forwarder function yourself but that's getting even more a hassle. Sometimes appropriate though.
Re: Using multiple mixin templates to implement operator overloading
On Saturday, 12 December 2020 at 18:14:31 UTC, Paul Backus wrote: IMO this is one of the stupider design decisions in D, but it's unlikely it will ever be fixed. The easiest workaround is to use string mixins instead, which work the way you'd expect them to. If issue 19365 got fixed, it could be done with an alias instead. https://issues.dlang.org/show_bug.cgi?id=19365 Currently string mixins are indeed the best option though.
Re: Using multiple mixin templates to implement operator overloading
On Saturday, 12 December 2020 at 18:14:31 UTC, Paul Backus wrote: Functions from different mixin templates can't overload each other. The reason for this is that, when you mix in a mixin template, it does not *actually* add the declarations inside it to a current scope: instead, it adds them to a new scope, and then (essentially) "imports" them into the current scope. Much appreciated! Exactly the explanation I needed.
Re: Using multiple mixin templates to implement operator overloading
On Saturday, 12 December 2020 at 17:36:57 UTC, Tobias Pankrath wrote: I want to wrap e.g. an int and implement basic arithmetic. In the provided example [1] I use two mixin templates to separately implement scaling (multiplication with int/double) and addition and subtraction with the type itself. In the end I want to have several distinct wrappers and allow specific operations between them and int / double. It's important that the return values are typed correctly, otherwise I could use std.typecons.Proxy. My problem is that both overloads of opBinary work, but not at the same time. As soon as I mixin both templates, they stop to work. If I just paste the implementation into the body of WrapInt, they work both at the same time though. Could someone explain the mechanics behind it? Thanks! [1] https://run.dlang.io/is/WbG987 Functions from different mixin templates can't overload each other. The reason for this is that, when you mix in a mixin template, it does not *actually* add the declarations inside it to a current scope: instead, it adds them to a new scope, and then (essentially) "imports" them into the current scope. IMO this is one of the stupider design decisions in D, but it's unlikely it will ever be fixed. The easiest workaround is to use string mixins instead, which work the way you'd expect them to.
Using multiple mixin templates to implement operator overloading
I want to wrap e.g. an int and implement basic arithmetic. In the provided example [1] I use two mixin templates to separately implement scaling (multiplication with int/double) and addition and subtraction with the type itself. In the end I want to have several distinct wrappers and allow specific operations between them and int / double. It's important that the return values are typed correctly, otherwise I could use std.typecons.Proxy. My problem is that both overloads of opBinary work, but not at the same time. As soon as I mixin both templates, they stop to work. If I just paste the implementation into the body of WrapInt, they work both at the same time though. Could someone explain the mechanics behind it? Thanks! [1] https://run.dlang.io/is/WbG987
Re: Template mixin + operator overloading question
On Friday, 11 October 2019 at 13:13:46 UTC, Dennis wrote: On Friday, 11 October 2019 at 12:45:59 UTC, Boyan Lazov wrote: Any ideas what I'm doing wrong? Nothing, it's a bug. https://issues.dlang.org/show_bug.cgi?id=19476 Alright, I see Well, the alias workaround works, so that seems just as good. Thanks!
Re: Template mixin + operator overloading question
On Friday, 11 October 2019 at 12:45:59 UTC, Boyan Lazov wrote: Any ideas what I'm doing wrong? Nothing, it's a bug. https://issues.dlang.org/show_bug.cgi?id=19476
Template mixin + operator overloading question
Hello, I seem to have a problem when I use a template mixin and then try to overload operators both in the mixin and in a struct from where it's instantiated. So the idea is that I have a few operators defined in the mixin, then a few more in the struct, and I want to forward all operations not explicitly defined in the struct to the ones in the mixin (and I don't want to do alias this for a variety of unrelated reasons). Basically the simplest code that gives me problems is: import std.stdio; mixin template Impl(T) { T v; int opBinary(string s: "+")(T other) { writeln("Single +"); return 0; } int opBinary(string s: "+")(T[] other) { writeln("Array +"); return 0; } } struct Pt { mixin Impl!float impl; int opBinary(string s: "*")(float other) { writeln("Single *"); return 0; } int opBinary(string s, T)(T v) { //Pt already has opBinary defined, so the operators in the mixin not visible //Thought that delegating to the mixin should be done this way writeln("Delegate ", s); return impl.opBinary!(s)(v); } } void main() { Pt pt; int r = pt + [1f, 2f]; writeln("R: ", r); } This results in an infinite loop though. Seems like Pt.opBinary!("+", float[]) is called over and over. Which is a bit un-intuitive to me - I'm not sure why calling impl.opBinary!(s) can result in endless recursion. The problem appears only when I have 2 operators in the mixin that I try to forward to - e.g. the two overloads of "+" in this case. If I have just 1, it works ok, if I have none, the error message is helpful. Any ideas what I'm doing wrong? Thanks!
Re: How to do operator overloading for <, >, <=, >=, !=, and == between struct and int?
On Sunday, 21 April 2019 at 18:17:00 UTC, Adam D. Ruppe wrote: On Sunday, 21 April 2019 at 18:07:08 UTC, Ferhat Kurtulmuş wrote: I am writing an opencv binding and need something like: Mat m = another_mat > 5; D does not support that. The comparison operators are always just true or false (as determined by the int opCmp or the bool opEquals returns), they do not return other types. You will probably have to use a named method instead. You could kinda fake it with a string template arg: Mat m = another_map.cmp!">"(5); But I'd probably just do it Mat m = another_map.is_greater_than(5); which imo is more readable. but the implementations are the same - in both cases, your custom function. I am a little disappointed :( I will go for named method instead. Thank you for a fast response.
Re: How to do operator overloading for <, >, <=, >=, !=, and == between struct and int?
On Sunday, 21 April 2019 at 18:07:08 UTC, Ferhat Kurtulmuş wrote: I am writing an opencv binding and need something like: Mat m = another_mat > 5; D does not support that. The comparison operators are always just true or false (as determined by the int opCmp or the bool opEquals returns), they do not return other types. You will probably have to use a named method instead. You could kinda fake it with a string template arg: Mat m = another_map.cmp!">"(5); But I'd probably just do it Mat m = another_map.is_greater_than(5); which imo is more readable. but the implementations are the same - in both cases, your custom function.
How to do operator overloading for <, >, <=, >=, !=, and == between struct and int?
I am writing an opencv binding and need something like: Mat m = another_mat > 5; Docs does not cover this sitiuation: https://dlang.org/spec/operatoroverloading.html#compare opBinary does not support those operators, and Section "Overloading <, <=, >, and >=" describes overloaded operators returning only int values. My struct is defined here: https://github.com/aferust/opencvd/blob/master/source/opencvd/cvcore.d
Re: Operator Overloading with multiple return types
On Fri, Mar 15, 2019 at 04:29:22PM -0700, Ali Çehreli via Digitalmars-d-learn wrote: > On 03/15/2019 03:48 PM, H. S. Teoh wrote: [...] > > Ali's example was unfortunately deceptively formatted. > > My editor did that. :) This is why I don't trust auto-formatters. ;-) > On my work computer, I've been experimenting with pulling the 'if', > 'in', etc to the same level as the function signature: > > int foo(T)(T t) > if (isBlah!T) > in (!t.empty) { > // ... > } [...] Phobos style also dictates that: int foo(T)(T t) if (isBlah!T) in (!t.empty) { ... } In my own code, however, I find it too block-of-text-y, so I prefer to indent it inwards: int foo(T)(T t) if (isBlah!T) in (!t.empty) { ... } But if your style puts { at the end of the line rather than the beginning, this could make it even easier to confuse for a statement inside the body. So YMMV. T -- IBM = I'll Buy Microsoft!
Re: Operator Overloading with multiple return types
On 03/15/2019 03:48 PM, H. S. Teoh wrote: On Fri, Mar 15, 2019 at 10:30:41PM +, eXodiquas via Digitalmars-d-learn wrote: On Friday, 15 March 2019 at 21:46:50 UTC, Ali Çehreli wrote: [...] Or use template constraints: struct Vector { Vector opBinary(string op)(Vector rhs) if (op == "+") { return Vector(); } double opBinary(string op)(Vector rhs) if (op == "/") { return 0.5; } } Ali Thanks for the quick and simple answers, but I don't get this one. If I do it that way the compiler doesn't know which function to call, or am I doing something wrong? Vector2 opBinary(string op)(Vector2 rhs) { if (op == "+") { return Vector2(this.x + rhs.x, this.y + rhs.y); } else if (op == "-") { return Vector2(this.x - rhs.x, this.y - rhs.y); } } float opBinary(string op)(Vector2 rhs) { if (op == "*") { return this.x * rhs.x + this.y * rhs.y; } } This gives me the error: overloads (Vector2 rhs) and (Vector2 rhs) both match argument list for opBinary [...] Ali's example was unfortunately deceptively formatted. My editor did that. :) On my work computer, I've been experimenting with pulling the 'if', 'in', etc to the same level as the function signature: int foo(T)(T t) if (isBlah!T) in (!t.empty) { // ... } Ali
Re: Operator Overloading with multiple return types
On Fri, Mar 15, 2019 at 10:30:41PM +, eXodiquas via Digitalmars-d-learn wrote: > On Friday, 15 March 2019 at 21:46:50 UTC, Ali Çehreli wrote: [...] > > Or use template constraints: > > > > struct Vector { > > Vector opBinary(string op)(Vector rhs) > > if (op == "+") { > > return Vector(); > > } > > > > double opBinary(string op)(Vector rhs) > > if (op == "/") { > > return 0.5; > > } > > } > > > > Ali > > Thanks for the quick and simple answers, but I don't get this one. If > I do it that way the compiler doesn't know which function to call, or > am I doing something wrong? > > Vector2 opBinary(string op)(Vector2 rhs) { > if (op == "+") { > return Vector2(this.x + rhs.x, this.y + rhs.y); > } else if (op == "-") { > return Vector2(this.x - rhs.x, this.y - rhs.y); > } > } > > float opBinary(string op)(Vector2 rhs) { > if (op == "*") { > return this.x * rhs.x + this.y * rhs.y; > } > } > > This gives me the error: > overloads (Vector2 rhs) and (Vector2 rhs) both match argument list for > opBinary [...] Ali's example was unfortunately deceptively formatted. The `if` has to be *outside* the function body; it's not a regular if-statement, but a signature constraint. And there is no `else` clause to it. Vector opBinary(string op)(Vector rhs) if (op == '+' || op == '-') { /* function body begins here */ ... } double opBinary(string op)(Vector rhs) if (op == '*') { /* function body begins here */ ... } T -- Notwithstanding the eloquent discontent that you have just respectfully expressed at length against my verbal capabilities, I am afraid that I must unfortunately bring it to your attention that I am, in fact, NOT verbose.
Re: Operator Overloading with multiple return types
On Fri, Mar 15, 2019 at 09:35:12PM +, eXodiquas via Digitalmars-d-learn wrote: [...] > Vector opBinary(string op)(Vector rhs) > { > static if (op == "+") return Vector(this.x + rhs.x, this.y + rhs.y); > else static if (op == "-") return Vector(this.x - rhs.x, this.y - > rhs.y); > } > > As you can see for the dot product the return type has to be a > float/double and not a vector. Is there any way to achive this > behaivour with D2? The opMul() function is not D2 style and I don't > want to use it. [...] Use signature constraints to declare different overloads depending on what the operator is. For example: Vector opBinary(string op)(Vector rhs) if (op == "+" || op == "=") { ... /* implement + and - here */ } double opBinary(string op)(Vector rhs) if (op == "*") { ... /* implement dot product here */ } T -- It is widely believed that reinventing the wheel is a waste of time; but I disagree: without wheel reinventers, we would be still be stuck with wooden horse-cart wheels.
Re: Operator Overloading with multiple return types
16.03.2019 1:30, eXodiquas пишет: On Friday, 15 March 2019 at 21:46:50 UTC, Ali Çehreli wrote: On 03/15/2019 02:43 PM, Sebastiaan Koppe wrote: On Friday, 15 March 2019 at 21:35:12 UTC, eXodiquas wrote: Is there any way to achive this behaivour with D2? Yep. Just make the return type in the function declaration `auto`. You are then free to return a different type in each static branch. Or use template constraints: struct Vector { Vector opBinary(string op)(Vector rhs) if (op == "+") { return Vector(); } double opBinary(string op)(Vector rhs) if (op == "/") { return 0.5; } } Ali Thanks for the quick and simple answers, but I don't get this one. If I do it that way the compiler doesn't know which function to call, or am I doing something wrong? Vector2 opBinary(string op)(Vector2 rhs) { if (op == "+") { return Vector2(this.x + rhs.x, this.y + rhs.y); } else if (op == "-") { return Vector2(this.x - rhs.x, this.y - rhs.y); } } float opBinary(string op)(Vector2 rhs) { if (op == "*") { return this.x * rhs.x + this.y * rhs.y; } } This gives me the error: overloads (Vector2 rhs) and (Vector2 rhs) both match argument list for opBinary eXodiquas You add wrong braces (`if` here is part of method signature not its body): ``` Vector2 opBinary(string op)(Vector2 rhs) if (op == "+") { return Vector2(this.x + rhs.x, this.y + rhs.y); } Vector2 opBinary(string op)(Vector2 rhs) if (op == "-") { return Vector2(this.x - rhs.x, this.y - rhs.y); } float opBinary(string op)(Vector2 rhs) if (op == "*") { return this.x * rhs.x + this.y * rhs.y; } ```
Re: Operator Overloading with multiple return types
On Friday, 15 March 2019 at 21:46:50 UTC, Ali Çehreli wrote: On 03/15/2019 02:43 PM, Sebastiaan Koppe wrote: On Friday, 15 March 2019 at 21:35:12 UTC, eXodiquas wrote: Is there any way to achive this behaivour with D2? Yep. Just make the return type in the function declaration `auto`. You are then free to return a different type in each static branch. Or use template constraints: struct Vector { Vector opBinary(string op)(Vector rhs) if (op == "+") { return Vector(); } double opBinary(string op)(Vector rhs) if (op == "/") { return 0.5; } } Ali Thanks for the quick and simple answers, but I don't get this one. If I do it that way the compiler doesn't know which function to call, or am I doing something wrong? Vector2 opBinary(string op)(Vector2 rhs) { if (op == "+") { return Vector2(this.x + rhs.x, this.y + rhs.y); } else if (op == "-") { return Vector2(this.x - rhs.x, this.y - rhs.y); } } float opBinary(string op)(Vector2 rhs) { if (op == "*") { return this.x * rhs.x + this.y * rhs.y; } } This gives me the error: overloads (Vector2 rhs) and (Vector2 rhs) both match argument list for opBinary eXodiquas
Re: Operator Overloading with multiple return types
On 03/15/2019 02:43 PM, Sebastiaan Koppe wrote: On Friday, 15 March 2019 at 21:35:12 UTC, eXodiquas wrote: Is there any way to achive this behaivour with D2? Yep. Just make the return type in the function declaration `auto`. You are then free to return a different type in each static branch. Or use template constraints: struct Vector { Vector opBinary(string op)(Vector rhs) if (op == "+") { return Vector(); } double opBinary(string op)(Vector rhs) if (op == "/") { return 0.5; } } Ali
Re: Operator Overloading with multiple return types
On Friday, 15 March 2019 at 21:35:12 UTC, eXodiquas wrote: Is there any way to achive this behaivour with D2? Yep. Just make the return type in the function declaration `auto`. You are then free to return a different type in each static branch.
Operator Overloading with multiple return types
Hi everyone, i'm currently working on a small physics engine and I thought it would be a nice feature to overload the operators of my vector struct so I don't have to make ugly function calls just to add and "multiply" my vectors. The problem now is that overloading the addition and subtraction of my vector struct is straight forward because both return and take a vector, dot product is not working for me because it is not possible to overload a function by return type (at least not that I am aware of). My code looks like the one from the dlang docs: Vector opBinary(string op)(Vector rhs) { static if (op == "+") return Vector(this.x + rhs.x, this.y + rhs.y); else static if (op == "-") return Vector(this.x - rhs.x, this.y - rhs.y); } As you can see for the dot product the return type has to be a float/double and not a vector. Is there any way to achive this behaivour with D2? The opMul() function is not D2 style and I don't want to use it. Thank you very much, eXodiquas
Re: Operator overloading for size_t
On Thursday, 14 March 2019 at 19:39:53 UTC, Alec Stewart wrote: For < and >, would one do this? I think you'd benefit a lot by reading http://ddili.org/ders/d.en/operator_overloading.html (just search for opCmp). I bet that will eliminate most of your confusion !
Re: Operator overloading for size_t
On Thursday, 14 March 2019 at 18:25:17 UTC, H. S. Teoh wrote: On Thu, Mar 14, 2019 at 06:07:46PM +, Alec Stewart via Digitalmars-d-learn wrote: [...] bool opEquals(ref const Interval i) const { // probably would be a bit more than just this, but for this issue // let's just stick with this. return d_start.opEquals(other.d_start) && d_end.opEquals(other.d_end); } There's no need to call opEquals explicitly like that. All you need to do is to use <, ==, and > as you normally would: bool opEquals(ref const Interval i) const { return d_start == other.d_start) && d_end == d_end; } T Thanks. I somehow managed to overthink this... For < and >, would one do this? size_t opCmp(ref const Interval other) const { return d_start < other.d_start; } size_t opCmp(ref const Interval other) const { return d_end < other.d_end; } size_t opCmp(ref const Interval other) const { return d_start > other.d_start; } size_t opCmp(ref const Interval other) const { return d_end > other.d_end; } Or would it better to do size_t opCmp(ref const Interval other) const { if (d_start < other.d_start) { return d_start < other.d_start; } else if (d_start > other.d_start) { return d_start > other.d_start; } else if (d_end < other.d_end) { return d_end < other.d_end; } else if (d_end > other.d_end) { return d_end > other.d_end; } else { return false; } }
Re: Operator overloading for size_t
On Thu, Mar 14, 2019 at 06:07:46PM +, Alec Stewart via Digitalmars-d-learn wrote: [...] > bool opEquals(ref const Interval i) const { > // probably would be a bit more than just this, but for this issue > // let's just stick with this. > return d_start.opEquals(other.d_start) && d_end.opEquals(other.d_end); > } There's no need to call opEquals explicitly like that. All you need to do is to use <, ==, and > as you normally would: bool opEquals(ref const Interval i) const { return d_start == other.d_start) && d_end == d_end; } T -- Без труда не выловишь и рыбку из пруда.
Re: Operator overloading for size_t
On Thursday, 14 March 2019 at 18:07:46 UTC, Alec Stewart wrote: // let's just stick with this. return d_start.opEquals(other.d_start) && d_end.opEquals(other.d_end); Why not just use d_start == other.d_start && d_end == other.d_end there? So should I bother with operator overloading here, or just make a member function? You shouldn't often call .opEquals yourself, just write a == b and let the compiler translate it if it needs to.
Operator overloading for size_t
I thought (for shits and giggles) to try and implement the Aho-Corasick algorithm[1]. I thought I'd start with a struct to represent the "interval": struct Interval { size_t d_start; size_t d_end; size_t size; this(size_t start, size_t end) { d_start = start; d_end = end; size = d_end - d_start + 1; } } It'd be useful to check for equality and inequality between instances of `Interval`, so I thought to use `.opEquals` for `d_start` and `d_end`. bool opEquals(ref const Interval i) const { // probably would be a bit more than just this, but for this issue // let's just stick with this. return d_start.opEquals(other.d_start) && d_end.opEquals(other.d_end); } But I do get an error saying `none of the overloads of `opEquals` are callable using argument types `(const(ulong), const(ulong))`, candidates are:` and it doesn't say the candidates. So should I bother with operator overloading here, or just make a member function? [1] https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_algorithm
Re: Templated operator overloading
On Wednesday, 22 August 2018 at 12:36:39 UTC, Simen Kjærås wrote: Since both your opOpAssigns match equally, the compiler throws up. The solution is to add some sort of restriction: This doesn't happen apparently: the operator has a left and a right side, even if both types define the operator, only one of them is on the left at each call. It works now after Ali corrected my stupid syntax :)
Re: Templated operator overloading
On Wednesday, 22 August 2018 at 13:20:01 UTC, aliak wrote: "void opOpAssign(string op, T)(ref Tthis, const ref T x)" looks like the wrong signature for opOpAssign. Oh I'll put on my stupid hat now... I realize I had copy-pasted the wrong syntax from the global function attempt, but I swear I thought I had re-typed and tested the right one... It's working now :)
Re: Templated operator overloading
On Wednesday, 22 August 2018 at 11:58:25 UTC, XavierAP wrote: I've been trying some things to template operator overloads. The reason is that I want very similar code for different types, but I can't use polymorphism, as they're structs rather than classes. Perhaps this choice is not as advantageous as I think, and I may change this design from structs to classes, or else the code duplication would be small and never subject to change. But now I'm just trying for the sake of learning to find out what works or not in terms of templated operator overloading, and whether the reason something doesn't work is by design and if mentioned in the specification, or just an arbitraty result of some unspecified parsing/lowering step order, or it depends on the compiler (I'm using dmd). [...] "void opOpAssign(string op, T)(ref Tthis, const ref T x)" looks like the wrong signature for opOpAssign. THink it needs to be: void opOpAssign(string op, T)(const ref T x) Then: mixin template operator! { void opOpAssign(string op, T)(const ref T x) { writeln(this, op, x); } } struct S1 { mixin operator; } struct S2 { mixin operator; } Cheers, - Ali
Re: Templated operator overloading
On Wednesday, 22 August 2018 at 11:58:25 UTC, XavierAP wrote: When I want to have the same operator overloading code in both types however, I can't make it work: From https://dlang.org/spec/operatoroverloading.html#binary: "the one with the ‘better’ match is selected. It is an error for both to equally match." Since both your opOpAssigns match equally, the compiler throws up. The solution is to add some sort of restriction: struct S1 { mixin opOver; } struct S2 { mixin opOver; } mixin template opOver() { auto opOpAssign(string op, T)(T rhs) if (T.stringof > typeof(this).stringof) { // Here import std.stdio; writeln(this, op, rhs); } } unittest { S1 a; S2 b; a += b; } And a final try with a global templated function instead of a mixin template: // private void opOpAssign(string op, Tthis, T)(ref Tthis that, const ref T x) This syntax would either enable the definition of operators on builtin types that shouldn't have them, or be perceived as inconsistent, so invasive operator overloading is used in D instead. -- Simen
Templated operator overloading
I've been trying some things to template operator overloads. The reason is that I want very similar code for different types, but I can't use polymorphism, as they're structs rather than classes. Perhaps this choice is not as advantageous as I think, and I may change this design from structs to classes, or else the code duplication would be small and never subject to change. But now I'm just trying for the sake of learning to find out what works or not in terms of templated operator overloading, and whether the reason something doesn't work is by design and if mentioned in the specification, or just an arbitraty result of some unspecified parsing/lowering step order, or it depends on the compiler (I'm using dmd). Since there are in my case two similar types (below just a minimal dumb proof of concept), I want the operator(s) to work within the same type, or also with the other. The following code actually works, including type parameter inferrence, and const ref to avoid struct copying: // import std.stdio; struct S1 { void opOpAssign(string op, T)(const ref T x) { writeln(this, op, x); } } struct S2 {} void main() { S1 s1; S2 s2; s1 *= s2; } // When I want to have the same operator overloading code in both types however, I can't make it work: // private mixin template operator(Tthis) { void opOpAssign(string op, T)(ref Tthis, const ref T x) { writeln(this, op, x); } } struct S1 { mixin operator!S1; } struct S2 { mixin operator!S2; } void main() { S1 s1; S2 s2; s1 *= s2; // Error: s1 *= s2 is not a scalar s1.opOpAssign!"*"(s2); // Error: template test.S1.operator!(S1).opOpAssign cannot deduce function } // And a final try with a global templated function instead of a mixin template: // private void opOpAssign(string op, Tthis, T)(ref Tthis that, const ref T x) { writeln(that, op, x); } struct S1 {} struct S2 {} void main() { S1 s1; S2 s2; s1 *= s2; // Error: s1 *= s2 is not a scalar s1.opOpAssign!"*"(s2); // OK! } //
Re: Using mixin templates for operator overloading.
On Sunday, 20 August 2017 at 12:46:59 UTC, Nicholas Wilson wrote: Did you try changing the `: "+"` constraints to `if` constraints? Yes. Yields the same result as this.
Re: Using mixin templates for operator overloading.
On Saturday, 19 August 2017 at 10:16:18 UTC, Balagopal Komarath wrote: Let us say I want to automatically define subtraction given that addition and negation are defined. I tried the following using mixin templates. If I simply mixin the template using "mixin sub;", then it gives the error [...] Did you try changing the `: "+"` constraints to `if` constraints?
Re: Using mixin templates for operator overloading.
On Saturday, 19 August 2017 at 10:16:18 UTC, Balagopal Komarath wrote: Let us say I want to automatically define subtraction given that addition and negation are defined. I tried the following using mixin templates... I assume there is no way to do this?
Using mixin templates for operator overloading.
Let us say I want to automatically define subtraction given that addition and negation are defined. I tried the following using mixin templates. If I simply mixin the template using "mixin sub;", then it gives the error tmpmixin.d(29): Error: incompatible types for ((a) - (b)): 'A!0' and 'A!0' I found out that mixin using an identifier for template mixins and then using an alias declaration as in the code given below can be used to bring in overloads. But, this produces the error. tmpmixin.d(23): Error: alias tmpmixin.A!0.A.opBinary conflicts with template tmpmixin.A!0.A.opBinary(string op : "+")(in A other) at tmpmixin.d(14) tmpmixin.d(29): Error: template instance tmpmixin.A!0 error instantiating As you can see, there is no conflict logically. One defines addition and the mixin defines subtraction. What is the right way to do this? mixin template sub() { alias T = typeof(this); T opBinary(string op : "-")(in T other) const { return this + (-other); } } struct A(int x) { int a; A opBinary(string op : "+")(in A other) const { return A(this.a + other.a); } A opUnary(string op : "-")() const { return A(-a); } mixin sub ops; alias opBinary = ops.opBinary; } void main() { import std.stdio : writeln; auto a = A!0(5), b = A!0(6); writeln(a-b); }
Re: Operator Overloading + / ~
On 6/19/17 8:16 AM, Martin Tschierschke wrote: Just a thought it might be useful for cut-n-paste when porting code: Would it be possible to define an operator overloading for '+'-Operator for strings to work as append? (like ~). I understand, that '~' is in general a better choice than '+', so this is of more theoretical interest. It is clear to me, that when I define my own string type this is possible. Regards mt. Just a further note, there is a reason + is not concatenation for arrays. does "1" + "1" = "2", or "11"? In some languages, both are possible outcomes, and which one is used depends on subtle differences in the context. With a specific operator to mean concatenation, the intent is clear. I would recommend you stick to using ~ for concatenation, even for your own types. -Steve
Re: Operator Overloading + / ~
On Monday, 19 June 2017 at 12:22:43 UTC, ketmar wrote: Martin Tschierschke wrote: Just a thought it might be useful for cut-n-paste when porting code: Would it be possible to define an operator overloading for '+'-Operator for strings to work as append? (like ~). I understand, that '~' is in general a better choice than '+', so this is of more theoretical interest. It is clear to me, that when I define my own string type this is possible. Regards mt. nope. it is not possible to overload operators for built-in types (and string is just an aliased array). Thank you for lightning fast response :-)
Re: Operator Overloading + / ~
Martin Tschierschke wrote: Just a thought it might be useful for cut-n-paste when porting code: Would it be possible to define an operator overloading for '+'-Operator for strings to work as append? (like ~). I understand, that '~' is in general a better choice than '+', so this is of more theoretical interest. It is clear to me, that when I define my own string type this is possible. Regards mt. nope. it is not possible to overload operators for built-in types (and string is just an aliased array).
Operator Overloading + / ~
Just a thought it might be useful for cut-n-paste when porting code: Would it be possible to define an operator overloading for '+'-Operator for strings to work as append? (like ~). I understand, that '~' is in general a better choice than '+', so this is of more theoretical interest. It is clear to me, that when I define my own string type this is possible. Regards mt.
Re: Generic operator overloading for immutable types?
On Tuesday, 13 June 2017 at 19:29:26 UTC, Gary Willoughby wrote: Is it possible for the `result` variable in the following code to be returned as an immutable type if it's created by adding two immutable types? Why do you even want that? Such plain data structure is implicitly convertible to any const flavor: https://dpaste.dzfl.pl/c59c4c7131b2
Re: Generic operator overloading for immutable types?
On 06/14/2017 03:47 AM, Steven Schveighoffer wrote: The fundamental difference is that const and immutable share a characteristic that mutable doesn't -- you can't mutate the data. (... through the reference at hand.) const and mutable share this: The data may be mutated from elsewhere. Mutable shares as much with const as immutable does. But it's the other side of const, of course. The reason const(inout) works is because const(immutable) evaluates to just immutable. const() just evaluates to const. In this way, mutable is less "special" than immutable. Yeah. That makes it impossible to express the proposed type. But it's just because of the immutable > const > mutable progression in D, which makes for arbitrary asymmetries between mutable and immutable. If it were const > mutable = immutable (and if we had a `mutable` keyword), then `inout immutable` would work like today's `inout const`, `inout mutable` would be what I'm talking about, and `inout const` would just be the same as const. That's not going to happen, of course. It would break everything. And I'm sure there are many obvious issues that I'm just ignoring here because the thing won't happen anyway. But it would make `inout foo` more symmetric. [...] The soundness of the function above seems good, but I don't know how to reason about the return type of f. Because mutable has no type modifier, it's hard to imagine doing this without one. And any time I think about how to define it, it breaks down. I can't imagine const(blah(T)) evaluating to mutable, no matter what blah is called, or how it works. Literally the ONLY place this would be useful is for inout functions, and Andrei pretty much has declared that inout should be completely stricken from Phobos/druntime in favor of templates. I can't imagine any leeway for another type modifier. const(inout) literally was an afterthought observation that we could relax the rules for this one case and get a little more usability. Ok, I think we're in agreement. The described variant of inout would make sense, but isn't possible in D. It would be very awkward to implement the feature without changing how mutability qualifiers interact. And changing that would be way too disruptive. It's also not at all clear that it would go well with the rest of the language.
Re: Generic operator overloading for immutable types?
On 6/13/17 7:51 PM, ag0aep6g wrote: On 06/14/2017 12:45 AM, Steven Schveighoffer wrote: No, the fact that immutable implicitly casts to const(inout) is a special property enabled by the knowledge that immutable data can NEVER change, so it's OK to assume it's (at least) const for all references. The same cannot be true of const or mutable. In other words: Immutable can't ever become mutable, at most it can become const. In the same vein: Mutable can't ever become immutable, at most it can become const. I don't see the fundamental difference. Mutable and immutable act very much alike. They're just on opposing ends of the scale. The fundamental difference is that const and immutable share a characteristic that mutable doesn't -- you can't mutate the data. The reason const(inout) works is because const(immutable) evaluates to just immutable. const() just evaluates to const. In this way, mutable is less "special" than immutable. This cannot work, because g() has no idea what the true mutability of x is. inout is not a template. This is why you can't implicitly cast inout to anything except const, and you can't implicitly cast anything to inout. I don't follow. `inout const` doesn't need a template to do its thing. Right, it's because the point at which the inout is "unwrapped" (i.e. at the point of return), it either becomes const or immutable. Inout functions can compile oblivious to how they will be called because the caller can completely determine the type that should be returned. It was the impetus to create inout in the first place -- why generate all these functions that do the same thing, just to change the return type, let the caller figure it out. I'm not sure what you mean about implicit conversions. Obviously, an inout result matches the corresponding inout argument(s). Doesn't matter if that's an implicit conversion or whatever. There is actually a difference. inout wraps one and exactly one type modifier. The table in that function shows what the modifier is depending on your collection of mutability parameters. If ALL of them are the same, it becomes that same thing. If any are different, you use the table to figure out. Most differences become const, some special cases become const(inout). This special case is solely to allow a function that takes both immutable and inout to potentially return immutable instead of just const. Now, this code could be made to work: bool condition; auto f(inout int* x) { int* y; /* mutable now */ return condition ? x : y; } void main() { int* r1 = f(new int); const r1 = f(new immutable int); } Mutable in, mutable out. Const in, const out. Immutable in, const out. You can't get immutable out, because y is mutable, and it cannot become immutable. Same principle as above. You never go from mutable to immutable or the other way around, so everything's fine, no? The soundness of the function above seems good, but I don't know how to reason about the return type of f. Because mutable has no type modifier, it's hard to imagine doing this without one. And any time I think about how to define it, it breaks down. I can't imagine const(blah(T)) evaluating to mutable, no matter what blah is called, or how it works. Literally the ONLY place this would be useful is for inout functions, and Andrei pretty much has declared that inout should be completely stricken from Phobos/druntime in favor of templates. I can't imagine any leeway for another type modifier. const(inout) literally was an afterthought observation that we could relax the rules for this one case and get a little more usability. -Steve
Re: Generic operator overloading for immutable types?
On 06/14/2017 12:45 AM, Steven Schveighoffer wrote: No, the fact that immutable implicitly casts to const(inout) is a special property enabled by the knowledge that immutable data can NEVER change, so it's OK to assume it's (at least) const for all references. The same cannot be true of const or mutable. In other words: Immutable can't ever become mutable, at most it can become const. In the same vein: Mutable can't ever become immutable, at most it can become const. I don't see the fundamental difference. Mutable and immutable act very much alike. They're just on opposing ends of the scale. This code doesn't work, and I see no way to make it work: inout(int*) f(inout int* x, inout int* y) { return y; } auto g(inout int* x) { int* y; return f(x, y); } void main() { int* x; int* r1 = g(x); } This cannot work, because g() has no idea what the true mutability of x is. inout is not a template. This is why you can't implicitly cast inout to anything except const, and you can't implicitly cast anything to inout. I don't follow. `inout const` doesn't need a template to do its thing. I'm not sure what you mean about implicit conversions. Obviously, an inout result matches the corresponding inout argument(s). Doesn't matter if that's an implicit conversion or whatever. The goal is something that works just like `inout const`, but switching mutable and immutable. I've realized that my example can be simplified: bool condition; auto f(inout int* x) { immutable int* y; return condition ? x : y; } void main() { const r1 = f(new int); immutable r2 = f(new immutable int); } That's `inout const` at work. Put a mutable or const int* in, you get a const int* out. Put immutable in, you get immutable out. You can't get mutable out, because the function might return y which is immutable, and it cannot become mutable. Now, this code could be made to work: bool condition; auto f(inout int* x) { int* y; /* mutable now */ return condition ? x : y; } void main() { int* r1 = f(new int); const r1 = f(new immutable int); } Mutable in, mutable out. Const in, const out. Immutable in, const out. You can't get immutable out, because y is mutable, and it cannot become immutable. Same principle as above. You never go from mutable to immutable or the other way around, so everything's fine, no? Except, there's no type in D that has this meaning.
Re: Generic operator overloading for immutable types?
On 6/13/17 5:58 PM, ag0aep6g wrote: On 06/13/2017 10:50 PM, Steven Schveighoffer wrote: const(inout) actually *is* a thing :) It's a type constructor that can be implicitly cast from immutable. This has advantages in some cases. See (horribly written) table at the bottom if the inout function section here: http://dlang.org/spec/function.html#inout-functions Also I talked about it last year at Dconf 2016: http://dconf.org/2016/talks/schveighoffer.html Huh. There it is. Took me some experimenting to understand what it does. If anyone else is interested, this is where it clicked for me: inout(int*) f(inout int* x, inout int* y) { return y; } inout(int*) g(inout int* x) { immutable int* y; return f(x, y); /* Error: cannot implicitly convert expression f(x, y) of type inout(const(int*)) to inout(int*) */ } That code can't compile because g's inout return type says that it has to return a mutable result for a mutable argument x. But y is immutable, so that can't work. We see in the error message that `f(x, y)` results in a `inout(const int*)`. We can change g's return type to that (or `auto`) and the code compiles. `inout const` enforces that the result is const or immutable. It cannot be mutable. This raises a question: There is no analogous inout variant that goes the other way (to mutable and const but not immutable), is there? No, the fact that immutable implicitly casts to const(inout) is a special property enabled by the knowledge that immutable data can NEVER change, so it's OK to assume it's (at least) const for all references. The same cannot be true of const or mutable. This code doesn't work, and I see no way to make it work: inout(int*) f(inout int* x, inout int* y) { return y; } auto g(inout int* x) { int* y; return f(x, y); } void main() { int* x; int* r1 = g(x); } This cannot work, because g() has no idea what the true mutability of x is. inout is not a template. This is why you can't implicitly cast inout to anything except const, and you can't implicitly cast anything to inout. So it MUST return const(int *) (if you did auto r1 = g(x), typeof(r1) would be const(int *)). -Steve
Re: Generic operator overloading for immutable types?
On 06/13/2017 10:50 PM, Steven Schveighoffer wrote: const(inout) actually *is* a thing :) It's a type constructor that can be implicitly cast from immutable. This has advantages in some cases. See (horribly written) table at the bottom if the inout function section here: http://dlang.org/spec/function.html#inout-functions Also I talked about it last year at Dconf 2016: http://dconf.org/2016/talks/schveighoffer.html Huh. There it is. Took me some experimenting to understand what it does. If anyone else is interested, this is where it clicked for me: inout(int*) f(inout int* x, inout int* y) { return y; } inout(int*) g(inout int* x) { immutable int* y; return f(x, y); /* Error: cannot implicitly convert expression f(x, y) of type inout(const(int*)) to inout(int*) */ } That code can't compile because g's inout return type says that it has to return a mutable result for a mutable argument x. But y is immutable, so that can't work. We see in the error message that `f(x, y)` results in a `inout(const int*)`. We can change g's return type to that (or `auto`) and the code compiles. `inout const` enforces that the result is const or immutable. It cannot be mutable. This raises a question: There is no analogous inout variant that goes the other way (to mutable and const but not immutable), is there? This code doesn't work, and I see no way to make it work: inout(int*) f(inout int* x, inout int* y) { return y; } auto g(inout int* x) { int* y; return f(x, y); } void main() { int* x; int* r1 = g(x); }
Re: Generic operator overloading for immutable types?
On 6/13/17 3:58 PM, ag0aep6g wrote: On 06/13/2017 09:29 PM, Gary Willoughby wrote: Is it possible for the `result` variable in the following code to be returned as an immutable type if it's created by adding two immutable types? Qualify the return type as `inout`: inout(Rational) opBinary(/*...*/)(/*...*/) inout {/*...*/} (That second `inout` is the same as the first one in your code.) Then you get a mutable/const/immutable result when you call the method on a mutable/const/immutable instance. It doesn't matter a lot, though. `Rational` is a value type, so you can convert the return value between qualifiers as you want, anyway. But it affects what you get with `auto`, of course. Yes exactly. I prefer personally to have value types always return mutable. You can always declare the result to be immutable or const, but declaring mutable is not as easy. e.g.: immutable ReallyLongValueTypeName foo1(); ReallyLongValueTypeName foo2(); version(bad) { auto x = foo1; ReallyLongValueTypeName y = foo1; } version(good) { immutable x = foo2; auto y = foo2; } [...] struct Rational { public long numerator; public long denominator; public inout Rational opBinary(string op)(inout(Rational) other) const `inout` and `const` kinda clash here. Both apply to `this`. But apparently, the compiler thinks `inout const` is a thing. No idea how it behaves. I don't think it's a thing in the language. As far as I understand, you should only put one of const/immutable/inout. const(inout) actually *is* a thing :) It's a type constructor that can be implicitly cast from immutable. This has advantages in some cases. See (horribly written) table at the bottom if the inout function section here: http://dlang.org/spec/function.html#inout-functions Also I talked about it last year at Dconf 2016: http://dconf.org/2016/talks/schveighoffer.html However, I find it very surprising that inout and const can combine the way you have written it. Even though it does make logical sense. I'd suspect it would be a good candidate for a "you didn't really mean that" error from the compiler, but it might be hard to decipher from the parsed code. -Steve
Re: Generic operator overloading for immutable types?
On 06/13/2017 09:29 PM, Gary Willoughby wrote: Is it possible for the `result` variable in the following code to be returned as an immutable type if it's created by adding two immutable types? Qualify the return type as `inout`: inout(Rational) opBinary(/*...*/)(/*...*/) inout {/*...*/} (That second `inout` is the same as the first one in your code.) Then you get a mutable/const/immutable result when you call the method on a mutable/const/immutable instance. It doesn't matter a lot, though. `Rational` is a value type, so you can convert the return value between qualifiers as you want, anyway. But it affects what you get with `auto`, of course. [...] struct Rational { public long numerator; public long denominator; public inout Rational opBinary(string op)(inout(Rational) other) const `inout` and `const` kinda clash here. Both apply to `this`. But apparently, the compiler thinks `inout const` is a thing. No idea how it behaves. I don't think it's a thing in the language. As far as I understand, you should only put one of const/immutable/inout.
Re: Generic operator overloading for immutable types?
On Tuesday, 13 June 2017 at 11:36:45 UTC, Steven Schveighoffer wrote: Nope, const works just fine. A clue is in your return type -- it's not inout! This should work: public Rational opBinary(string op)(Rational rhs) const If Rational had any indirections, then inout would be required, and your signature wouldn't work (because you can't convert an inout(SomethingWithPointers) to SomethingWithPointers). Why do I need to make the member function const, but not the parameter? Because the parameter is taken by value, 'this' is a reference. -Steve Is it possible for the `result` variable in the following code to be returned as an immutable type if it's created by adding two immutable types? import std.stdio; struct Rational { public long numerator; public long denominator; public inout Rational opBinary(string op)(inout(Rational) other) const { static if (op == "+") { return inout(Rational)(0, 0); } else { static assert(0, "Operator '" ~ op ~ "' not implemented"); } } } unittest { auto baz= immutable(Rational)(1, 3); auto qux= immutable(Rational)(1, 6); auto result = baz + qux; writeln(typeof(result).stringof); // Result is not immutable! }
Re: Generic operator overloading for immutable types?
On 6/12/17 3:36 PM, H. S. Teoh via Digitalmars-d-learn wrote: On Mon, Jun 12, 2017 at 07:38:44PM +, Gary Willoughby via Digitalmars-d-learn wrote: In the following code is there any way to make the `opBinary` method generic to be able to accept immutable as well as a standard type? The code currently passes the unit test but I wonder if I could get rid of the duplication to overload the operator? I'm failing badly. This is what inout was designed for: public inout Rational opBinary(string op)(inout Rational rhs) { static if (op == "+") { return inout(Rational)(0, 0); } else { static assert(0, "Operator '" ~ op ~ "' not implemented"); } } That should do the trick. Nope, const works just fine. A clue is in your return type -- it's not inout! This should work: public Rational opBinary(string op)(Rational rhs) const If Rational had any indirections, then inout would be required, and your signature wouldn't work (because you can't convert an inout(SomethingWithPointers) to SomethingWithPointers). Why do I need to make the member function const, but not the parameter? Because the parameter is taken by value, 'this' is a reference. -Steve
Re: Generic operator overloading for immutable types?
On 6/12/17 3:51 PM, Gary Willoughby wrote: I don't know how H. S. Teoh managed to answer 'before' I posted but thanks guys! :) D programmers are *that* good. Seriously though, for NNTP connections, timestamp is taken from the submitter's PC. -Steve
Re: Generic operator overloading for immutable types?
On Monday, 12 June 2017 at 20:10:17 UTC, H. S. Teoh wrote: Therefore, nowadays I always recommend writing parenthesis with type modifiers, so that the intent it unambiguous, i.e., always write `inout(Rational)` rather than `inout Rational`, unless you intend for `inout` to apply to the function rather than the return value. (And I apologize for the slip-up in my code example above, where I failed to do this for the function parameter.) T Ok, thanks.
Re: Generic operator overloading for immutable types?
On Mon, Jun 12, 2017 at 01:08:13PM -0700, Ali Çehreli via Digitalmars-d-learn wrote: > On 06/12/2017 01:03 PM, Gary Willoughby wrote: > > On Monday, 12 June 2017 at 19:36:52 UTC, H. S. Teoh wrote: > > >> public inout Rational opBinary(string op)(inout Rational rhs) > > > Quick question about the signature, if I change it to (note the parens): > > > >public inout(Rational) opBinary(string op)(inout(Rational) rhs) > > > > It no longer works, why is that? > > That's frequently faced issue with D. :) In the first declaration, > inout applies to the member function. More precisely, it applies to the `this` reference implicitly passed to the member function. You need inout to apply to `this`, otherwise a.opBinary(b) won't work when a is an immutable instance. > In the second one, it applies only to the return type. Walter has his > rationale for doing it that way but it's confusing. [...] A few years ago we tried lobbying for the language to enforce putting modifiers that only apply to the function itself on the far right side, e.g.: public Rational opBinary(string op)(inout Rational rhs) inout But it was rejected for various reasons. Therefore, nowadays I always recommend writing parenthesis with type modifiers, so that the intent it unambiguous, i.e., always write `inout(Rational)` rather than `inout Rational`, unless you intend for `inout` to apply to the function rather than the return value. (And I apologize for the slip-up in my code example above, where I failed to do this for the function parameter.) T -- Маленькие детки - маленькие бедки.
Re: Generic operator overloading for immutable types?
On 06/12/2017 01:03 PM, Gary Willoughby wrote: > On Monday, 12 June 2017 at 19:36:52 UTC, H. S. Teoh wrote: >> public inout Rational opBinary(string op)(inout Rational rhs) > Quick question about the signature, if I change it to (note the parens): > >public inout(Rational) opBinary(string op)(inout(Rational) rhs) > > It no longer works, why is that? That's frequently faced issue with D. :) In the first declaration, inout applies to the member function. In the second one, it applies only to the return type. Walter has his rationale for doing it that way but it's confusing. Ali
Re: Generic operator overloading for immutable types?
On Monday, 12 June 2017 at 19:36:52 UTC, H. S. Teoh wrote: On Mon, Jun 12, 2017 at 07:38:44PM +, Gary Willoughby via Digitalmars-d-learn wrote: In the following code is there any way to make the `opBinary` method generic to be able to accept immutable as well as a standard type? The code currently passes the unit test but I wonder if I could get rid of the duplication to overload the operator? I'm failing badly. This is what inout was designed for: public inout Rational opBinary(string op)(inout Rational rhs) { static if (op == "+") { return inout(Rational)(0, 0); } else { static assert(0, "Operator '" ~ op ~ "' not implemented"); } } That should do the trick. T Quick question about the signature, if I change it to (note the parens): public inout(Rational) opBinary(string op)(inout(Rational) rhs) It no longer works, why is that?
Re: Generic operator overloading for immutable types?
On Monday, 12 June 2017 at 19:51:37 UTC, Gary Willoughby wrote: I don't know how H. S. Teoh managed to answer 'before' I posted but thanks guys! :) might be a bug, happened here http://forum.dlang.org/post/ohbr5l$2mng$1...@digitalmars.com also.
Re: Generic operator overloading for immutable types?
I don't know how H. S. Teoh managed to answer 'before' I posted but thanks guys! :)
Re: Generic operator overloading for immutable types?
Gary Willoughby wrote: In the following code is there any way to make the `opBinary` method generic to be able to accept immutable as well as a standard type? The code currently passes the unit test but I wonder if I could get rid of the duplication to overload the operator? I'm failing badly. public inout(Rational) opBinary(string op)(inout(Rational) rhs) inout { static if (op == "+") { return Rational(0, 0); } else { static assert(0, "Operator '" ~ op ~ "' not implemented"); } }
Re: Generic operator overloading for immutable types?
On Mon, Jun 12, 2017 at 07:38:44PM +, Gary Willoughby via Digitalmars-d-learn wrote: > In the following code is there any way to make the `opBinary` method > generic to be able to accept immutable as well as a standard type? The > code currently passes the unit test but I wonder if I could get rid of > the duplication to overload the operator? I'm failing badly. This is what inout was designed for: public inout Rational opBinary(string op)(inout Rational rhs) { static if (op == "+") { return inout(Rational)(0, 0); } else { static assert(0, "Operator '" ~ op ~ "' not implemented"); } } That should do the trick. T -- Frank disagreement binds closer than feigned agreement.
Generic operator overloading for immutable types?
In the following code is there any way to make the `opBinary` method generic to be able to accept immutable as well as a standard type? The code currently passes the unit test but I wonder if I could get rid of the duplication to overload the operator? I'm failing badly. import std.stdio; struct Rational { public long numerator; public long denominator; public immutable Rational opBinary(string op)(immutable Rational rhs) { static if (op == "+") { return Rational(0, 0); } else { static assert(0, "Operator '" ~ op ~ "' not implemented"); } } public Rational opBinary(string op)(Rational rhs) { static if (op == "+") { return Rational(0, 0); } else { static assert(0, "Operator '" ~ op ~ "' not implemented"); } } } unittest { auto foo = Rational(1, 3); auto bar = Rational(1, 6); writefln("%s", foo + bar); auto baz = immutable Rational(1, 3); auto qux = immutable Rational(1, 6); writefln("%s", baz + qux); }
Re: Operator overloading through UFCS doesn't work
On Tuesday, May 31, 2016 14:11:58 ixid via Digitalmars-d-learn wrote: > On Sunday, 29 May 2016 at 07:18:10 UTC, Jonathan M Davis wrote: > > And the fact that allowing free functions to overload operators > > via UFCS sends us into that territory just highlights the fact > > that they're a horrible idea. > > > > - Jonathan M Davis > > Do you have any examples of UFCS doing bad things? Most people > seem to very much like it yet you argue against any change that > would benefit UFCS. > > You seem to prefer: > > read(to(easier(much(i over i.much.easier.to.read The primary benefit of UFCS is that you can write generic code that will work with both member functions and free functions, allowing you to have a free function that does something and a member function that does that same thing more efficiently for that specific type (a prime example of this would be a function like find where a linear search make sense in most cases but wouldn't for certain data structures - e.g. a sorted, binary tree). So, the "universal" aspect of UFCS is important for generic code, whereas it would be completely unnecessary if the code weren't generic. All of the other benefits of UFCS are highly subjective and have to do with what a particular person thinks is easier or harder to read rather than actual, technical benefits (though obviously writing code in a way that is easier to read for those working on it is obviously valuable). Personally, I've dealt with functional languages enough that I've never felt that UFCS was much of an improvement syntactically. But we have it, and anyone is free to use it or not as they see fit. Regardless, what I'm arguing against here is altering operator overloading so that it works with free functions via UFCS instead of requiring that it be part of the type. It's a terrible idea IMHO to allow random code to add an overloaded operator to a type rather having it actually be part of the type's design, and in addition to that, it doesn't play at all nicely with symbol conflicts, because you're using an operator rather than a function, meaning that not only do you have no way to specify which version of the overloaded operator code should use, but it would completely defeat the purpose of using an overloaded operator in the first place even if you could. But fortunately, Walter agrees with me (or at least did, the last time the subject came up in the newsgroup), so I don't think that I have to worry about overloaded operators be definable via free functions. - Jonathan M Davis
Re: Operator overloading through UFCS doesn't work
On Sunday, 29 May 2016 at 07:18:10 UTC, Jonathan M Davis wrote: And the fact that allowing free functions to overload operators via UFCS sends us into that territory just highlights the fact that they're a horrible idea. - Jonathan M Davis Do you have any examples of UFCS doing bad things? Most people seem to very much like it yet you argue against any change that would benefit UFCS. You seem to prefer: read(to(easier(much(i over i.much.easier.to.read
Re: Operator overloading through UFCS doesn't work
Here's one more vote for extending UFCS to operator overloading. Elie wrote that it's "a restriction that seems pointless and arbitrary"... which summarizes my own thoughts rather well, too. There are certainly concerning scenarios that can arise from making this change, but the correct way to approach this problem is not to tell the programmer "I won't let you use that tool, because if you mishandle it then you might find yourself in a nasty mess." That's what Java does - it treats the programmer like an idiot - and that's why it's so universally despised. It has consistently been my impression that this is very much not the sort of philosophy D follows. Anyway, D already provides the programmer with a wealth of tools which, if mishandled, can place them in a nasty mess. So I think this is a poor rationale for withholding from the programmer one more.
Re: Operator overloading through UFCS doesn't work
On Sunday, 29 May 2016 at 07:18:10 UTC, Jonathan M Davis wrote: On Friday, May 27, 2016 09:08:20 Marc Schütz via Digitalmars-d-learn wrote: On Thursday, 26 May 2016 at 06:23:17 UTC, Jonathan M Davis wrote: > The difference is that it's impossible to do > 10.opBinary!"+"(15), so if you're forced to do > foo.opBinary!"+"(bar) to get around a symbol conflict, it > won't > work with built-in types. Well, that begs the question: Why don't built-in types define `opBinary`? That's just another arbitrary irregularity, isn't it. It was never intended that any op* function be called by anyone except where the compiler lowers code to use them. They're for declaring overloaded operators on user-defined types so that those types can be used with those operators. If you're calling opBinary in your own code, you're doing it wrong. And it would be downright silly to then add opBinary to the built-in types. If I were to design my own language from scratch, that's actually how I would do it. All operators, even for built-in types, would just be syntax sugar for the method calls. The goal should be to minimize the difference between built-in and user-defined types as much as possible. Turtles all the way down... They don't need operator overloading. They already have the operators. Operators are supposed to be used as operators, not functions, and if there's any need to use them as functions, then there's something seriously wrong. And the fact that allowing free functions to overload operators via UFCS sends us into that territory just highlights the fact that they're a horrible idea. I'd say the fact that it doesn't work, and can't currently work for the reasons you described, points to an inconsistency in the language's design. It means that we have two largely overlapping concepts (builtin types and user defined types), where most language features work the same for both, but some don't. That's not the end of the world, of course, but still...
Re: Legal operator overloading
On Monday, 30 May 2016 at 05:54:42 UTC, Nicholas Wilson wrote: Is it legal/possible to overload the unary * operator? Also is it legal/possible to individually overload the comparison operators and not return a bool? Yes to unary * (see [1]). No to the rest. Comparisons are always lowered to opEquals or opCmp by the compiler as per [2]. [1] https://dlang.org/spec/operatoroverloading.html#unary [2] https://dlang.org/spec/operatoroverloading.html#eqcmp
Legal operator overloading
Is it legal/possible to overload the unary * operator? Also is it legal/possible to individually overload the comparison operators and not return a bool? (Before you ask no I'm not crazy, I am trying to make a library solution to multiple address spaces for supporting OpenCL/CUDA in D, and vector comparisons.)
Re: Operator overloading through UFCS doesn't work
On Friday, May 27, 2016 09:08:20 Marc Schütz via Digitalmars-d-learn wrote: > On Thursday, 26 May 2016 at 06:23:17 UTC, Jonathan M Davis wrote: > > The difference is that it's impossible to do > > 10.opBinary!"+"(15), so if you're forced to do > > foo.opBinary!"+"(bar) to get around a symbol conflict, it won't > > work with built-in types. > > Well, that begs the question: Why don't built-in types define > `opBinary`? That's just another arbitrary irregularity, isn't it. It was never intended that any op* function be called by anyone except where the compiler lowers code to use them. They're for declaring overloaded operators on user-defined types so that those types can be used with those operators. If you're calling opBinary in your own code, you're doing it wrong. And it would be downright silly to then add opBinary to the built-in types. They don't need operator overloading. They already have the operators. Operators are supposed to be used as operators, not functions, and if there's any need to use them as functions, then there's something seriously wrong. And the fact that allowing free functions to overload operators via UFCS sends us into that territory just highlights the fact that they're a horrible idea. - Jonathan M Davis
Re: Operator overloading through UFCS doesn't work
On Thursday, 26 May 2016 at 06:23:17 UTC, Jonathan M Davis wrote: The difference is that it's impossible to do 10.opBinary!"+"(15), so if you're forced to do foo.opBinary!"+"(bar) to get around a symbol conflict, it won't work with built-in types. Well, that begs the question: Why don't built-in types define `opBinary`? That's just another arbitrary irregularity, isn't it.
Re: Operator overloading through UFCS doesn't work
On 25.05.2016 01:19, Elie Morisse wrote: On Saturday, 13 October 2012 at 22:58:56 UTC, Timon Gehr wrote: Afaik free-function operator overloads (but not in the context of UFCS) were considered and turned down because D did not want to get amidst discussions about adding Koenig lookup. UFCS does not do Koenig lookup. I don't get it, aren't the current symbol lookup rules enough to make free function operator overloads useful? To me it looks like they are. ... Yup. It could be argued that it is essentially a compiler bug. Sorry for digging up this thread, just getting irritated by a restriction that seems pointless and arbitrary. ... It is, but it has a few vocal proponents. Overloaded operators would suffer from the same potential abuses other methods are subjected to if UFCS was enabled, nothing more as far as I can see. You are perfectly right of course. It's painful for no benefit. (For example, there's no way to overload e.g. '+=' for classes in a way that reassigns the reference.)
Re: Operator overloading through UFCS doesn't work
On Thursday, May 26, 2016 16:24:37 Elie Morisse via Digitalmars-d-learn wrote: > On Thursday, 26 May 2016 at 06:23:17 UTC, Jonathan M Davis wrote: > > The difference is that it's impossible to do > > 10.opBinary!"+"(15), so if you're forced to do > > foo.opBinary!"+"(bar) to get around a symbol conflict, it won't > > work with built-in types. > > Obviously operator overloading should be limited to class and > struct types, so that's not really relevant. It's completely relevent, because generic code is frequently going to operate on a variety of types - including both built-in types and user-defined types. If code is going to use +, then + must work. opBinary!"+" is simply not going to cut it. With UFCS, if there's a potential symbol conflict, then you can work around it - even in generic code. But with an overloaded operator, the operator must work as an operator, or generic code simply won't work. > > UFCS does not have that problem, because you're dealing with > > free functions and can choose to not use UFCS and provide the > > full import path or to alias the function, which you can't do > > with operators - particularly built-in operators. > > I still don't understand what you're getting at, unfortunately. You don't _have_ to use UFCS. You can call the function as a normal free function with the full import path. You can also alias the function to a different name and use that with UFCS or import the conflicting function with an alias so that it doesn't conflict. The overloaded operator, on the other hand, needs to work as on overloaded operator, and we can't just call an overloaded operator with its full import path or alias it when importing it, because it's an overloaded operator and not just a free function. We have ways to distinguish conflicting types with free functions, and we don't with operators, because operators were designed to be part of the type and not free functions. And syntactically, it really doesn't work to provide a way to distinguish between conflicting versions of an overloaded operator, because then you're no longer just using the operator and might as well just be using a function. But all I'm really doing here is saying the same thing multiple times in slightly different ways, so if you don't get it, I don't know how to explain it. > Thanks for taking the time to explain, although I still fail to > see a good justification for disabling UFCS for operators. I will > look for more discussions on the topic and if still no opposing > argument seems valid I might push the issue forward. You're going to need more than a good reason why it shouldn't be allowed. You're going to need a good reason why it should be, and I really don't think that you're going to have one good enough to get you anywhere with Walter. I'm fairly certain that there's a thread or two floating around somewhere in the main newsgroup where he's responded to the issue before though. - Jonathan M Davis
Re: Operator overloading through UFCS doesn't work
On Thursday, 26 May 2016 at 06:23:17 UTC, Jonathan M Davis wrote: The difference is that it's impossible to do 10.opBinary!"+"(15), so if you're forced to do foo.opBinary!"+"(bar) to get around a symbol conflict, it won't work with built-in types. Obviously operator overloading should be limited to class and struct types, so that's not really relevant. UFCS does not have that problem, because you're dealing with free functions and can choose to not use UFCS and provide the full import path or to alias the function, which you can't do with operators - particularly built-in operators. I still don't understand what you're getting at, unfortunately. D was designed to be much cleaner with operator overloading than C++ is. It restricts what the definitions of the operators are so that you don't have to define as many of them to get the basic operations (e.g. opCmp for most of the comparison operators or op!"++" for both pre-increment and post-increment) and so that they aren't easily overloaded to do stuff that does not correspond to what that operator does for built-in types. D doesn't even use + for string concatenation, because Walter thought that that was operator abuse. Allowing arbitrary code to add overloaded operators to an existing type is not at all in line with that philosophy. Regardless, there really isn't much point in arguing this. If you want things to change, you're going to need to convince Walter, which I very much doubt is going to happen. - Jonathan M Davis Thanks for taking the time to explain, although I still fail to see a good justification for disabling UFCS for operators. I will look for more discussions on the topic and if still no opposing argument seems valid I might push the issue forward.
Re: Operator overloading through UFCS doesn't work
On Wednesday, May 25, 2016 23:31:18 Elie Morisse via Digitalmars-d-learn wrote: > On Wednesday, 25 May 2016 at 21:50:06 UTC, Jonathan M Davis wrote: > > It's not an overloaded operator anymore at that point, and that > > definitely fails to work for generic code, since not all > > operators are overloaded operators. Free functions don't have > > that problem. > > Sorry to reiterate the previous post but is that really the case? > >void FuncTemplate(...)(...) { > X.FreeFunc(Y); >} > >import ModuleA; // contains FreeFunc >import ModuleB; // contains a conflicting FreeFunc overload > >FuncTemplate!()(); // fails > > Where is the difference with writing generic code with operators > (overloaded or not)? The difference is that it's impossible to do 10.opBinary!"+"(15), so if you're forced to do foo.opBinary!"+"(bar) to get around a symbol conflict, it won't work with built-in types. UFCS does not have that problem, because you're dealing with free functions and can choose to not use UFCS and provide the full import path or to alias the function, which you can't do with operators - particularly built-in operators. D was designed to be much cleaner with operator overloading than C++ is. It restricts what the definitions of the operators are so that you don't have to define as many of them to get the basic operations (e.g. opCmp for most of the comparison operators or op!"++" for both pre-increment and post-increment) and so that they aren't easily overloaded to do stuff that does not correspond to what that operator does for built-in types. D doesn't even use + for string concatenation, because Walter thought that that was operator abuse. Allowing arbitrary code to add overloaded operators to an existing type is not at all in line with that philosophy. Regardless, there really isn't much point in arguing this. If you want things to change, you're going to need to convince Walter, which I very much doubt is going to happen. - Jonathan M Davis
Re: Operator overloading through UFCS doesn't work
On Wednesday, 25 May 2016 at 21:50:06 UTC, Jonathan M Davis wrote: It's not an overloaded operator anymore at that point, and that definitely fails to work for generic code, since not all operators are overloaded operators. Free functions don't have that problem. Sorry to reiterate the previous post but is that really the case? void FuncTemplate(...)(...) { X.FreeFunc(Y); } import ModuleA; // contains FreeFunc import ModuleB; // contains a conflicting FreeFunc overload FuncTemplate!()(); // fails Where is the difference with writing generic code with operators (overloaded or not)?
Re: Operator overloading through UFCS doesn't work
On Wednesday, May 25, 2016 15:46:23 Elie Morisse via Digitalmars-d-learn wrote: > On Tuesday, 24 May 2016 at 23:43:46 UTC, Jonathan M Davis wrote: > > On Tuesday, May 24, 2016 23:19:32 Elie Morisse via > > > > Digitalmars-d-learn wrote: > >> On Saturday, 13 October 2012 at 22:58:56 UTC, Timon Gehr wrote: > >> > Afaik free-function operator overloads (but not in the > >> > context of UFCS) were considered and turned down because D > >> > did not want to get amidst discussions about adding Koenig > >> > lookup. UFCS does not do Koenig lookup. > >> > >> I don't get it, aren't the current symbol lookup rules enough > >> to make free function operator overloads useful? To me it > >> looks like they are. > >> > >> Sorry for digging up this thread, just getting irritated by a > >> restriction that seems pointless and arbitrary. > >> > >> Overloaded operators would suffer from the same potential > >> abuses other methods are subjected to if UFCS was enabled, > >> nothing more as far as I can see. > > > > If UFCS doesn't work, because there are two free functions with > > the same name which take the same arguments, then you can > > differentiate between them by not using UFCS and using their > > full import paths, or you can alias them so that they don't > > have the same name. Neither of those would be possible with > > operator overloading. If overloaded operators were allowed as > > free functions, then if there were ever a symbol conflict, > > you'd be screwed. > > > > - Jonathan M Davis > >X.FreeFunc(Y); // multiple matches error >ModuleA.FreeFunc(X, Y); // ok > >X * Y; // multiple matches error >ModuleA.opBinary!'*'(X, Y); // ok > > Is there much of a difference between the two? It's not an overloaded operator anymore at that point, and that definitely fails to work for generic code, since not all operators are overloaded operators. Free functions don't have that problem. - Jonathan M Davis
Re: Operator overloading through UFCS doesn't work
On Tuesday, 24 May 2016 at 23:43:46 UTC, Jonathan M Davis wrote: On Tuesday, May 24, 2016 23:19:32 Elie Morisse via Digitalmars-d-learn wrote: On Saturday, 13 October 2012 at 22:58:56 UTC, Timon Gehr wrote: > Afaik free-function operator overloads (but not in the > context of UFCS) were considered and turned down because D > did not want to get amidst discussions about adding Koenig > lookup. UFCS does not do Koenig lookup. I don't get it, aren't the current symbol lookup rules enough to make free function operator overloads useful? To me it looks like they are. Sorry for digging up this thread, just getting irritated by a restriction that seems pointless and arbitrary. Overloaded operators would suffer from the same potential abuses other methods are subjected to if UFCS was enabled, nothing more as far as I can see. If UFCS doesn't work, because there are two free functions with the same name which take the same arguments, then you can differentiate between them by not using UFCS and using their full import paths, or you can alias them so that they don't have the same name. Neither of those would be possible with operator overloading. If overloaded operators were allowed as free functions, then if there were ever a symbol conflict, you'd be screwed. - Jonathan M Davis X.FreeFunc(Y); // multiple matches error ModuleA.FreeFunc(X, Y); // ok X * Y; // multiple matches error ModuleA.opBinary!'*'(X, Y); // ok Is there much of a difference between the two?
Re: Operator overloading through UFCS doesn't work
On Tuesday, May 24, 2016 23:19:32 Elie Morisse via Digitalmars-d-learn wrote: > On Saturday, 13 October 2012 at 22:58:56 UTC, Timon Gehr wrote: > > Afaik free-function operator overloads (but not in the context > > of UFCS) were considered and turned down because D did not want > > to get amidst discussions about adding Koenig lookup. UFCS does > > not do Koenig lookup. > > I don't get it, aren't the current symbol lookup rules enough to > make free function operator overloads useful? To me it looks like > they are. > > Sorry for digging up this thread, just getting irritated by a > restriction that seems pointless and arbitrary. > > Overloaded operators would suffer from the same potential abuses > other methods are subjected to if UFCS was enabled, nothing more > as far as I can see. If UFCS doesn't work, because there are two free functions with the same name which take the same arguments, then you can differentiate between them by not using UFCS and using their full import paths, or you can alias them so that they don't have the same name. Neither of those would be possible with operator overloading. If overloaded operators were allowed as free functions, then if there were ever a symbol conflict, you'd be screwed. - Jonathan M Davis
Re: Operator overloading through UFCS doesn't work
On Saturday, 13 October 2012 at 22:58:56 UTC, Timon Gehr wrote: Afaik free-function operator overloads (but not in the context of UFCS) were considered and turned down because D did not want to get amidst discussions about adding Koenig lookup. UFCS does not do Koenig lookup. I don't get it, aren't the current symbol lookup rules enough to make free function operator overloads useful? To me it looks like they are. Sorry for digging up this thread, just getting irritated by a restriction that seems pointless and arbitrary. Overloaded operators would suffer from the same potential abuses other methods are subjected to if UFCS was enabled, nothing more as far as I can see.