www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - opIndexAddAssign? ...

reply Manfred Nowak <svv1999 hotmail.com> writes:
On writing the implementation of a map, where the Valuetype is a set, I 
had to stop on recognizing, that only opIndexAssign is permitted.

I.e.:
  map[ key]+= element;
has no possible overload.

OpIndex delivers an rvalue and the only possibility to get an lvalue is 
in conjunction with a plain assignment.

Why arbitrary restrictions when the goal is to have great 
expressiveness? 

-manfred
Feb 09 2007
next sibling parent reply Lionello Lunesu <lio lunesu.remove.com> writes:
Manfred Nowak wrote:
 On writing the implementation of a map, where the Valuetype is a set, I 
 had to stop on recognizing, that only opIndexAssign is permitted.
 
 I.e.:
   map[ key]+= element;
 has no possible overload.
 
 OpIndex delivers an rvalue and the only possibility to get an lvalue is 
 in conjunction with a plain assignment.
 
 Why arbitrary restrictions when the goal is to have great 
 expressiveness? 
 
 -manfred

opIndexAssign was needed because D has no "inout" return type. It's a hack, if you ask me. L.
Feb 09 2007
next sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Lionello Lunesu" <lio lunesu.remove.com> wrote in message 
news:eqhnb7$2tjh$2 digitaldaemon.com...

 opIndexAssign was needed because D has no "inout" return type. It's a 
 hack, if you ask me.

Ehhh, I don't really think so.. having opIndexAssign is nice because you can do some processing on the value when it's assigned. How, for example would you do something as simple as this: void opIndexAssign(int value, int key) { mData[key] = value % 10; } using inout return values? In almost every one of my opIndexAssign overloads, I do some kind of checking or processing on the value before inserting it into the internal container.
Feb 09 2007
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote
 How, for example would you do something as simple as
 this: 
 
 void opIndexAssign(int value, int key)
 {
     mData[key] = value % 10;
 }
 
 using inout return values?

The question is: what do really want to do and why do you have to mix up your intentions? Your intention seems to bind an operation to the valuetype of the AA. But you have bound it to the AA directly and not to the valuetype. The code instance[ key]= value; should be called approximately as instance.opIndex_l(key)= typeof( mData[key]).opAssign( value) In fact your example is not that easy because it is unclear what the return value of opIndex( int) would be: mData[key] or mData[key] * 10 or void as your opIndexAssign suggests? -manfred
Feb 09 2007
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Manfred Nowak" <svv1999 hotmail.com> wrote in message 
news:eqi90k$kf5$1 digitaldaemon.com...

 Your intention seems to bind an operation to the valuetype of the AA.
 But you have bound it to the AA directly and not to the valuetype.

So, if all we had was opIndex that returned inout, you're suggesting I implement a new class for the value type of the internal container every time I want the behavior of index assigning to change? That seems a little overkill. Besides, the code I posted is an example. I'm just trying to say that an inout-returning-opIndex doesn't allow you to do anything with the value that's added to this.
 The code
  instance[ key]= value;
 should be called approximately as
  instance.opIndex_l(key)= typeof( mData[key]).opAssign( value)

Again, if my internal data structure for instance is just an: int[] mData; then I can't have the "mData[key] = value % 10;" like in the opIndexAssign version. I'll have to create a proxy class to hold the data which modulos its value by 10 whenever opAssign is used, and make an array of that, and create all those class instances. God, it's starting to sound like Java.
 In fact your example is not that easy because it is unclear what the
 return value of opIndex( int) would be:
  mData[key]
 or
  mData[key] * 10

That's not what I'm really getting at.. I'm talking about adding or setting an index of a container type, not getting one.
 or void as your opIndexAssign suggests?

I find chaining assignments and using assignments in conditionals (i.e. "if((x = nextThing()) == 0)") to be in bad form, so I always make my opXxxAssign and property setters return void so you can't do that. It's just a personal preference.
Feb 09 2007
parent Manfred Nowak <svv1999 hotmail.com> writes:
Jarrett Billingsley wrote

 I'll have to create a proxy class to hold
 the data which modulos its value by 10 whenever opAssign is used,
 and make an array of that, and create all those class instances. 

opIndex<op>Assign, which D currently not has.
 I'm talking about adding
 or setting an index of a container type, not getting one.

Andrei already pointed out | Probably the principled thing to do is to allow two functions, one | that returns lvalue and one that returns an rvalue, and have the | compiler pick the appropriate one. there might be a difference between the representations of data inside and outside of classes. -manfred
Feb 09 2007
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Lionello Lunesu wrote:
 Manfred Nowak wrote:
 On writing the implementation of a map, where the Valuetype is a set, 
 I had to stop on recognizing, that only opIndexAssign is permitted.

 I.e.:
   map[ key]+= element;
 has no possible overload.

 OpIndex delivers an rvalue and the only possibility to get an lvalue 
 is in conjunction with a plain assignment.

 Why arbitrary restrictions when the goal is to have great expressiveness?
 -manfred

opIndexAssign was needed because D has no "inout" return type. It's a hack, if you ask me.

It is a hack, but a useful one IMO. For example, say I want to insert an element in an AA only if the AA itself is used as an lvalue. ie. auto x = myAA[key]; // return default value if not present myAA[key] = x; // insert and assign I'll admit that it would be much nicer if this worked for all applicable operator overloads without requiring separate signatures for each, but I'll take what I can get :-) Sean
Feb 09 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Sean Kelly wrote:
 Lionello Lunesu wrote:
 Manfred Nowak wrote:
 On writing the implementation of a map, where the Valuetype is a set, 
 I had to stop on recognizing, that only opIndexAssign is permitted.

 I.e.:
   map[ key]+= element;
 has no possible overload.

 OpIndex delivers an rvalue and the only possibility to get an lvalue 
 is in conjunction with a plain assignment.

 Why arbitrary restrictions when the goal is to have great 
 expressiveness?
 -manfred

opIndexAssign was needed because D has no "inout" return type. It's a hack, if you ask me.

It is a hack, but a useful one IMO. For example, say I want to insert an element in an AA only if the AA itself is used as an lvalue. ie. auto x = myAA[key]; // return default value if not present myAA[key] = x; // insert and assign I'll admit that it would be much nicer if this worked for all applicable operator overloads without requiring separate signatures for each, but I'll take what I can get :-)

It's also useful for sparse vectors and matrices: you don't want to autoinsert stuff or throw when all you want is to read. Just return zero if it's not there. Probably the principled thing to do is to allow two functions, one that returns lvalue and one that returns an rvalue, and have the compiler pick the appropriate one. Andrei
Feb 09 2007
prev sibling next sibling parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
Manfred Nowak Wrote:

 On writing the implementation of a map, where the Valuetype is a 
 set, I had to stop on recognizing, that only opIndexAssign is 
 permitted.

To add opIndexAddAssign et al would be a step back towards the old C++ nightmare of providing lots of operator overloads where just one should suffice.
 I.e.:
     map[key]+= element;
 has no possible overload.
 
 OpIndex delivers an rvalue and the only possibility to get an 
 lvalue is in conjunction with a plain assignment.
 
 Why arbitrary restrictions when the goal is to have great 
 expressiveness?

I agree. It's been said about properties enough times, and the same should apply to array indexes. That is, qwert[yuiop] op= asdfg should be, in the lack of any overriding alternative (such as an op<op>Assign on the type of qwert[yuiop]), equivalent to qwert[yuiop] = qwert[yuiop] op asdfg except that qwert and yuiop are evaluated only once. Stewart.
Feb 09 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Stewart Gordon wrote:
 Manfred Nowak Wrote:
 
 On writing the implementation of a map, where the Valuetype is a 
 set, I had to stop on recognizing, that only opIndexAssign is 
 permitted.

To add opIndexAddAssign et al would be a step back towards the old C++ nightmare of providing lots of operator overloads where just one should suffice.
 I.e.:
     map[key]+= element;
 has no possible overload.

 OpIndex delivers an rvalue and the only possibility to get an 
 lvalue is in conjunction with a plain assignment.

 Why arbitrary restrictions when the goal is to have great 
 expressiveness?

I agree. It's been said about properties enough times, and the same should apply to array indexes. That is, qwert[yuiop] op= asdfg should be, in the lack of any overriding alternative (such as an op<op>Assign on the type of qwert[yuiop]), equivalent to qwert[yuiop] = qwert[yuiop] op asdfg except that qwert and yuiop are evaluated only once.

Same with .length. arr.length += 2; Andrei
Feb 09 2007
prev sibling parent BCS <BCS pathlink.com> writes:
Manfred Nowak wrote:
 On writing the implementation of a map, where the Valuetype is a set, I 
 had to stop on recognizing, that only opIndexAssign is permitted.
 
 I.e.:
   map[ key]+= element;
 has no possible overload.
 
 OpIndex delivers an rvalue and the only possibility to get an lvalue is 
 in conjunction with a plain assignment.
 
 Why arbitrary restrictions when the goal is to have great 
 expressiveness? 
 
 -manfred

It could have some nasty side effect but, have opIndex return a struct/object that shadows the original and overloads opAddAssign. class F { T[] i class N { this(inout T l){j=&l;} T* j; T opAddAssign(T k) { j+=k; return this; } } N opIndex(int ind) { return new N(i[ind]); } } On second thought, don't
Feb 09 2007