digitalmars.D - Operator overhaul
- Bill Baxter (20/28) Oct 21 2008 (Reply to message on d.announce)
- Andrei Alexandrescu (4/38) Oct 21 2008 There would be composition (of the kind usually addressed through
- bearophile (5/7) Oct 21 2008 You really want to make D an awesome language :-)
- Andrei Alexandrescu (17/28) Oct 21 2008 I agree. Anyhow, the floor is open for suggestions on how to overhaul
- bearophile (8/18) Oct 21 2008 I'm a newbie still regarding such stuff.
- Andrei Alexandrescu (13/42) Oct 21 2008 Well it's great you are improving on that kind of knowledge. It's also
- Michel Fortin (32/49) Oct 21 2008 Not terribly difficult. Add the op keyword, which could work both as a
- BCS (9/43) Oct 21 2008 I want to do:
- Christian Kamm (8/24) Oct 21 2008 Maybe that could be done similarly to boost.operators
- Don (13/43) Oct 22 2008 My earlier contribution is in bugzilla 124, which would eliminate all
- BCS (2/5) Oct 21 2008 what is that? Link?
- Andrei Alexandrescu (7/15) Oct 21 2008 The classic introduction:
- Russell Lewis (11/45) Oct 22 2008 While we're at it, we need to think about lazy slicing (with $). Say
- bearophile (6/7) Oct 23 2008 If I understand what you are talking about, then you may want to look at...
- Sergey Gromov (4/17) Oct 23 2008 If you have a lazy array, its opSlice(size_t, size_t) can return another...
- Russell Lewis (9/26) Oct 23 2008 The problem is that when you have an opSlice which isn't $-aware, the co...
- Christopher Wright (11/47) Nov 01 2008 I can never remember how the values for comparison work. An enum
-
Stewart Gordon
(11/21)
Nov 01 2008
"Christopher Wright"
wrote in message - Denis Koroskin (13/33) Nov 01 2008 I often use the following pattern:
- Stewart Gordon (9/19) Nov 01 2008 "Denis Koroskin" <2korden@gmail.com> wrote in message
- Christopher Wright (19/39) Nov 01 2008 This would mainly be for writing opCmp, and as documentation. As an
(Reply to message on d.announce) On Wed, Oct 22, 2008 at 9:36 AM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Jason House wrote:O_o! Oh noohs! We may have to start calling it the "E" language soon. Seriously, though, if the overhaul makes it possible to do multiple slices with multiple $'s, then I'm all for it. As in hyperArray[$-1, 1..$, 0..$-2] Even better would be python's "just leave it out" slicing: As in hyperArray[$-1, 1.., ..$-2] It's a bit easier to read in my eyes. There was also the stuff discussed before about passing in the op as a delegate or template parameter to give the class designer a chance to run some pre- or post- op code as desired. Or rewriting x+=y type ops to be x=x+y So we have: 1. multiple slice indexing 2. generalized $ indexing 3. generalized op execution Any other operator-related issues? --bbI think the entire operator paraphernalia is due for a serious overhaul.Is there a good reason why it shouldn't be possible to use opAssign as a replacement for opIndexAssign? --bbopindexAssign will still be needed when opindex has a non-ref return type.
Oct 21 2008
Bill Baxter wrote:(Reply to message on d.announce) On Wed, Oct 22, 2008 at 9:36 AM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:There would be composition (of the kind usually addressed through expression templates), but we don't have a good solution to that yet. AndreiJason House wrote:O_o! Oh noohs! We may have to start calling it the "E" language soon. Seriously, though, if the overhaul makes it possible to do multiple slices with multiple $'s, then I'm all for it. As in hyperArray[$-1, 1..$, 0..$-2] Even better would be python's "just leave it out" slicing: As in hyperArray[$-1, 1.., ..$-2] It's a bit easier to read in my eyes. There was also the stuff discussed before about passing in the op as a delegate or template parameter to give the class designer a chance to run some pre- or post- op code as desired. Or rewriting x+=y type ops to be x=x+y So we have: 1. multiple slice indexing 2. generalized $ indexing 3. generalized op execution Any other operator-related issues?I think the entire operator paraphernalia is due for a serious overhaul.Is there a good reason why it shouldn't be possible to use opAssign as a replacement for opIndexAssign? --bbopindexAssign will still be needed when opindex has a non-ref return type.
Oct 21 2008
Andrei Alexandrescu:There would be composition (of the kind usually addressed through expression templates), but we don't have a good solution to that yet.You really want to make D an awesome language :-) I though that in D the expression templates stuff was left to be done by the AST macros, but if they don't come until D3, then a more specialized functionality may be used in the meantime... (if necessary even to be later removed in D3). Bye, bearophile
Oct 21 2008
bearophile wrote:Andrei Alexandrescu:I agree. Anyhow, the floor is open for suggestions on how to overhaul the entire operator business. I'll tell you what is, to me, clearly dead wrong: whenever one implements any class that wants to overload a bunch of operators (number-like classes etc.) inevitably it boils down to repetitive code like this: struct Whatever { Whatever opAdd(Whatever rhs) { return member + rhs.member; } Whatever opSubtract(Whatever rhs) { return member - rhs.member; } ... } and lines and lines of brain damaged repetition, repetition, repetition. If we add opWhateverAssign that will get even worse. The challenge is, then, to define a way to overload operators that makes the classic applications of the feature short and sweet. AndreiThere would be composition (of the kind usually addressed through expression templates), but we don't have a good solution to that yet.You really want to make D an awesome language :-) I though that in D the expression templates stuff was left to be done by the AST macros, but if they don't come until D3, then a more specialized functionality may be used in the meantime... (if necessary even to be later removed in D3).
Oct 21 2008
Andrei Alexandrescu:struct Whatever { Whatever opAdd(Whatever rhs) { return member + rhs.member; } Whatever opSubtract(Whatever rhs) { return member - rhs.member; } ... } and lines and lines of brain damaged repetition, repetition, repetition. If we add opWhateverAssign that will get even worse. The challenge is, then, to define a way to overload operators that makes the classic applications of the feature short and sweet.I'm a newbie still regarding such stuff. At a first sight a possible solution is to resort to mathematics: state somehow that class has the property of being a Number, maybe Additive too, then such methods become automatically defined for free... :-) I think functional languages have similar things, it may be named structural subtyping... I am too much ignorant about this topics, I am sorry. Once I know Haskell well enough I'll be able to talk about this stuff much better. I suggest you to ask this question in the "Lambda the Ultimate" blog, they are quite expert of such matters, and they are very willing to help. If you say them you want to make D "more functional" they will work for you in the nights too for free :-) At the moment D1 has class mixins to do something like that, they can find the name of the class by themselves too. Bye, bearophile
Oct 21 2008
bearophile wrote:Andrei Alexandrescu:Well it's great you are improving on that kind of knowledge. It's also very nice that you acknowledge lack of expertise in the matter (something very rare on the Usenet and in this group too) and finally it is nice that in the same post you _do_ give advice, which suggests there may be a contradiction somewhere :o).struct Whatever { Whatever opAdd(Whatever rhs) { return member + rhs.member; } Whatever opSubtract(Whatever rhs) { return member - rhs.member; } ... } and lines and lines of brain damaged repetition, repetition, repetition. If we add opWhateverAssign that will get even worse. The challenge is, then, to define a way to overload operators that makes the classic applications of the feature short and sweet.I'm a newbie still regarding such stuff. At a first sight a possible solution is to resort to mathematics: state somehow that class has the property of being a Number, maybe Additive too, then such methods become automatically defined for free... :-) I think functional languages have similar things, it may be named structural subtyping... I am too much ignorant about this topics, I am sorry. Once I know Haskell well enough I'll be able to talk about this stuff much better.I suggest you to ask this question in the "Lambda the Ultimate" blog, they are quite expert of such matters, and they are very willing to help. If you say them you want to make D "more functional" they will work for you in the nights too for free :-) At the moment D1 has class mixins to do something like that, they can find the name of the class by themselves too. Bye, bearophileOur problem is not that we're out of ideas, but instead that there's an embarrassment of riches in terms of the particular solution to adopt. LtU is an excellent forum populated by many Illuminati, yet sometimes discussions could get tedious because they become overly general. For example: "I want to do this for D." "Wait, why doesn't D make everything a reference? That would certainly make things cleaner" etc. Andrei
Oct 21 2008
On 2008-10-21 22:24:08 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said:I agree. Anyhow, the floor is open for suggestions on how to overhaul the entire operator business. I'll tell you what is, to me, clearly dead wrong: whenever one implements any class that wants to overload a bunch of operators (number-like classes etc.) inevitably it boils down to repetitive code like this: struct Whatever { Whatever opAdd(Whatever rhs) { return member + rhs.member; } Whatever opSubtract(Whatever rhs) { return member - rhs.member; } ... } and lines and lines of brain damaged repetition, repetition, repetition. If we add opWhateverAssign that will get even worse. The challenge is, then, to define a way to overload operators that makes the classic applications of the feature short and sweet.Not terribly difficult. Add the op keyword, which could work both as a function name and an operator substitute inside the operator function: struct Whatever { Whatever op(+,-,/,*)(Whatever rhs) { member op rhs.member; return this; } auto op(=,+=,-=,/=,*=)(Whatever rhs) { member op rhs.member; return member; } auto op([])(int index, T value) { return member[index]; } // This one is a template: auto op([]=,[]+=,[]-=,[]/=,[]*=)(T)(int index, T value) { return member[index] op value; } } The sad thing is that then we lose the meaningful names, so perhaps this would be more in order: struct Whatever { Whatever opAdd,opSub,opDiv,opMul(Whatever rhs) { op(member, rhs.member); return this; } auto opAssign,opAddAssign,opSubAssign,opDivAssign,opMulAssign(Whatever rhs) { op(member, rhs.member); return member; } auto opIndex(int index, T value) { return member[index]; } // This one is a template: auto opIndexAssign,opIndexAddAssign,opIndexSubAssign,opIndexDivAssign,opI dexMulAssign(T)(int index, T value) { return op(member[index], value); } } Hum, no, sadly this one doesn't cut it. :-/ -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Oct 21 2008
Reply to Andrei,bearophile wrote:I want to do: typedef real MyReal(int i) { MyReal!(i) opAdd(MyReal!(i)); //default impl } If left without a body this would /only/ redefine the allowed operators and the types they result in. The actual code gen would be exactly the same. Allowing bodies might be cool.Andrei Alexandrescu:I agree. Anyhow, the floor is open for suggestions on how to overhaul the entire operator business. I'll tell you what is, to me, clearly dead wrong: whenever one implements any class that wants to overload a bunch of operators (number-like classes etc.) inevitably it boils down to repetitive code like this: struct Whatever { Whatever opAdd(Whatever rhs) { return member + rhs.member; } Whatever opSubtract(Whatever rhs) { return member - rhs.member; } ... } and lines and lines of brain damaged repetition, repetition, repetition. If we add opWhateverAssign that will get even worse. The challenge is, then, to define a way to overload operators that makes the classic applications of the feature short and sweet. AndreiThere would be composition (of the kind usually addressed through expression templates), but we don't have a good solution to that yet.You really want to make D an awesome language :-) I though that in D the expression templates stuff was left to be done by the AST macros, but if they don't come until D3, then a more specialized functionality may be used in the meantime... (if necessary even to be later removed in D3).
Oct 21 2008
Andrei Alexandrescu wrote:I'll tell you what is, to me, clearly dead wrong: whenever one implements any class that wants to overload a bunch of operators (number-like classes etc.) inevitably it boils down to repetitive code like this: struct Whatever { Whatever opAdd(Whatever rhs) { return member + rhs.member; } Whatever opSubtract(Whatever rhs) { return member - rhs.member; } ... } and lines and lines of brain damaged repetition, repetition, repetition. If we add opWhateverAssign that will get even worse. The challenge is, then, to define a way to overload operators that makes the classic applications of the feature short and sweet.Maybe that could be done similarly to boost.operators (http://www.boost.org/doc/libs/1_36_0/libs/utility/operators.htm), only using template mixins? The main problem I see with any solution is that it will need to be pretty complicated to accomodate for all cases and be generally useful. In the end, understanding what the mixins do exactly may take longer than having a set of repetitive, but trivial overloads.
Oct 21 2008
Andrei Alexandrescu wrote:bearophile wrote:My earlier contribution is in bugzilla 124, which would eliminate all unnecessary temporaries, though it doesn't address the repetitive code issue (in fact it makes the repitition slightly worse). It seems clear that we need to restrict some of the useless freedoms that exist at present: (1) a OP= b _must_ mean a = a OP b (allowing the compiler to convert from one to the other); (2) The return value of x OP= y must be x. Beyond this it's not quite so obvious. I wish we could get rid of opPostInc/Dec, with their ridiculous semantics. (Convert it to preInc, generate an error if the return value is used).Andrei Alexandrescu:I agree. Anyhow, the floor is open for suggestions on how to overhaul the entire operator business. I'll tell you what is, to me, clearly dead wrong: whenever one implements any class that wants to overload a bunch of operators (number-like classes etc.) inevitably it boils down to repetitive code like this: struct Whatever { Whatever opAdd(Whatever rhs) { return member + rhs.member; } Whatever opSubtract(Whatever rhs) { return member - rhs.member; } ... } and lines and lines of brain damaged repetition, repetition, repetition. If we add opWhateverAssign that will get even worse. The challenge is, then, to define a way to overload operators that makes the classic applications of the feature short and sweet.There would be composition (of the kind usually addressed through expression templates), but we don't have a good solution to that yet.You really want to make D an awesome language :-) I though that in D the expression templates stuff was left to be done by the AST macros, but if they don't come until D3, then a more specialized functionality may be used in the meantime... (if necessary even to be later removed in D3).
Oct 22 2008
Reply to Andrei,There would be composition (of the kind usually addressed through expression templates), but we don't have a good solution to that yet.what is that? Link?
Oct 21 2008
BCS wrote:Reply to Andrei,The classic introduction: http://ubiety.uwaterloo.ca/~tveldhui/papers/Expression-Templates/exprtmpl.html And another one: http://www.angelikalanger.com/Articles/Cuj/ExpressionTemplates/ExpressionTemplates.htm Googling for <<expression templates c++>> yields some more. AndreiThere would be composition (of the kind usually addressed through expression templates), but we don't have a good solution to that yet.what is that? Link?
Oct 21 2008
Bill Baxter wrote:(Reply to message on d.announce) On Wed, Oct 22, 2008 at 9:36 AM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:While we're at it, we need to think about lazy slicing (with $). Say that you have an lazy array type (where it only generates the members when required). In that sort of situation, you don't necessarily want to generate the entire array (so that you can calculate the length) the first time that somebody does a = a[0..$-1]; or tmp = a[$-1]; You want to be able to keep a reference that says "so many characters from the end of the array, though the length of the array isn't known yet."Jason House wrote:O_o! Oh noohs! We may have to start calling it the "E" language soon. Seriously, though, if the overhaul makes it possible to do multiple slices with multiple $'s, then I'm all for it. As in hyperArray[$-1, 1..$, 0..$-2] Even better would be python's "just leave it out" slicing: As in hyperArray[$-1, 1.., ..$-2] It's a bit easier to read in my eyes. There was also the stuff discussed before about passing in the op as a delegate or template parameter to give the class designer a chance to run some pre- or post- op code as desired. Or rewriting x+=y type ops to be x=x+y So we have: 1. multiple slice indexing 2. generalized $ indexing 3. generalized op execution Any other operator-related issues?I think the entire operator paraphernalia is due for a serious overhaul.Is there a good reason why it shouldn't be possible to use opAssign as a replacement for opIndexAssign? --bbopindexAssign will still be needed when opindex has a non-ref return type.
Oct 22 2008
Russell Lewis:While we're at it, we need to think about lazy slicing (with $).If I understand what you are talking about, then you may want to look at islice() of Python and xslice() of my libs. You can give a lazy sequence and specify the slicing: xslice(someiterable, 10, 20) => computes the first 20 items, ignores the first 10, and returns an array of the second 10. Adding a syntax to D to do that, similar to the array slicing seems like a nice thing. Bye, bearophile
Oct 23 2008
Wed, 22 Oct 2008 21:22:09 -0700, Russell Lewis wrote:If you have a lazy array, its opSlice(size_t, size_t) can return another lazy array, properly configured. What's the problem?Any other operator-related issues?While we're at it, we need to think about lazy slicing (with $). Say that you have an lazy array type (where it only generates the members when required). In that sort of situation, you don't necessarily want to generate the entire array (so that you can calculate the length) the first time that somebody does a = a[0..$-1]; or tmp = a[$-1]; You want to be able to keep a reference that says "so many characters from the end of the array, though the length of the array isn't known yet."
Oct 23 2008
Sergey Gromov wrote:Wed, 22 Oct 2008 21:22:09 -0700, Russell Lewis wrote:The problem is that when you have an opSlice which isn't $-aware, the code a = a[0..$-1]; gets converted by the compiler into a = a[0..a.length-1]; which, of course, means that our array isn't lazy anymore. Ideally, opSlice should have variants which allow the data structure to say "I will remember to never return you the last few elements" without having to calculate the array length at slice-time.If you have a lazy array, its opSlice(size_t, size_t) can return another lazy array, properly configured. What's the problem?Any other operator-related issues?While we're at it, we need to think about lazy slicing (with $). Say that you have an lazy array type (where it only generates the members when required). In that sort of situation, you don't necessarily want to generate the entire array (so that you can calculate the length) the first time that somebody does a = a[0..$-1]; or tmp = a[$-1]; You want to be able to keep a reference that says "so many characters from the end of the array, though the length of the array isn't known yet."
Oct 23 2008
Bill Baxter wrote:(Reply to message on d.announce) On Wed, Oct 22, 2008 at 9:36 AM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:I can never remember how the values for comparison work. An enum somewhere (except that's a new type, so just constants): struct Comparison { const int LeftIsGreater = -1; const int Equal = 0; const int RightIsGreater = 1; } That's simple, it doesn't require any compiler changes, and it'll save me some headaches.Jason House wrote:O_o! Oh noohs! We may have to start calling it the "E" language soon. Seriously, though, if the overhaul makes it possible to do multiple slices with multiple $'s, then I'm all for it. As in hyperArray[$-1, 1..$, 0..$-2] Even better would be python's "just leave it out" slicing: As in hyperArray[$-1, 1.., ..$-2] It's a bit easier to read in my eyes. There was also the stuff discussed before about passing in the op as a delegate or template parameter to give the class designer a chance to run some pre- or post- op code as desired. Or rewriting x+=y type ops to be x=x+y So we have: 1. multiple slice indexing 2. generalized $ indexing 3. generalized op execution Any other operator-related issues? --bbI think the entire operator paraphernalia is due for a serious overhaul.Is there a good reason why it shouldn't be possible to use opAssign as a replacement for opIndexAssign? --bbopindexAssign will still be needed when opindex has a non-ref return type.
Nov 01 2008
"Christopher Wright" <dhasenan gmail.com> wrote in message news:gei556$1sgc$3 digitalmars.com... <snip>I can never remember how the values for comparison work. An enum somewhere (except that's a new type, so just constants): struct Comparison { const int LeftIsGreater = -1; const int Equal = 0; const int RightIsGreater = 1; } That's simple, it doesn't require any compiler changes, and it'll save me some headaches.Enums are implicitly convertible to their underlying types, so there's no point in using a struct instead. If you're taking advantage of the ability to return an arbitrary int for performance, are you planning to multiply by one of these constants? Stewart. -- My e-mail address is valid but not my primary mailbox. Please keep replies on the 'group where everybody may benefit.
Nov 01 2008
On Sat, 01 Nov 2008 23:42:30 +0300, Stewart Gordon <smjg_1998 yahoo.com> wrote:"Christopher Wright" <dhasenan gmail.com> wrote in message news:gei556$1sgc$3 digitalmars.com... <snip>I often use the following pattern: [code] auto possibleResults[3] = [ Result1, Result2, Result3, ]; int cmp = compare(a, b) + 1; // cmp is 0, 1 or 2 return possibleResults[cmp]; [/code] instead of bunch of ifs.I can never remember how the values for comparison work. An enum somewhere (except that's a new type, so just constants): struct Comparison { const int LeftIsGreater = -1; const int Equal = 0; const int RightIsGreater = 1; } That's simple, it doesn't require any compiler changes, and it'll save me some headaches.Enums are implicitly convertible to their underlying types, so there's no point in using a struct instead. If you're taking advantage of the ability to return an arbitrary int for performance, are you planning to multiply by one of these constants? Stewart.
Nov 01 2008
"Denis Koroskin" <2korden gmail.com> wrote in message news:op.ujyj0qpio7cclz proton.creatstudio.intranet... <snip>I often use the following pattern: [code] auto possibleResults[3] = [ Result1, Result2, Result3, ]; int cmp = compare(a, b) + 1; // cmp is 0, 1 or 2 return possibleResults[cmp]; [/code]So you rely on custom-made compare functions that always return -1, 0 or 1? In this case, why not make them return 0, 1 or 2 in the first place? Stewart. -- My e-mail address is valid but not my primary mailbox. Please keep replies on the 'group where everybody may benefit.
Nov 01 2008
Stewart Gordon wrote:"Christopher Wright" <dhasenan gmail.com> wrote in message news:gei556$1sgc$3 digitalmars.com... <snip>I didn't realize that.I can never remember how the values for comparison work. An enum somewhere (except that's a new type, so just constants): struct Comparison { const int LeftIsGreater = -1; const int Equal = 0; const int RightIsGreater = 1; } That's simple, it doesn't require any compiler changes, and it'll save me some headaches.Enums are implicitly convertible to their underlying types, so there's no point in using a struct instead.If you're taking advantage of the ability to return an arbitrary int for performance, are you planning to multiply by one of these constants?This would mainly be for writing opCmp, and as documentation. As an alternative: struct Comparison { static Comparison LeftIsGreater = {-1}; static Comparison Equal = {0}; static Comparison RightIsGreater = {1}; int value; bool opEquals(int i) { if (value == 0 && i == 0) return true; if (value < 0 && i < 0) return true; if (value > 0 && i > 0) return true; return false; } }
Nov 01 2008