Revised RFC on range design for D2

Sergey Gromov snake.scaly at gmail.com
Fri Oct 3 13:23:15 PDT 2008


Sat, 4 Oct 2008 04:34:46 +0900,
Bill Baxter wrote:
> On Fri, Oct 3, 2008 at 11:43 PM, Sergey Gromov <snake.scaly at gmail.com> wrote:
> > Fri, 03 Oct 2008 20:59:54 +0800,
> > KennyTM~ wrote:
> >> Sergey Gromov wrote:
> >> > What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?
> >>
> >> Probably performance.
> >>
> >> Consider seeking to the end of a 100M-node single-linked list, and
> >> increase its content by 1.
> >>
> >> But I agree that if something like .opIndexAddAssign() is not defined,
> >> the compiler should fall back to use a.opIndexAssign(b, a.opIndex(b)+c).
> >>
> >> (The same idea can be extended to properties and .opSlice() )
> >
> > No, if you want performance in this particular case you define
> >
> > ref int opIndex()
> >
> > because I think whenever compiler encounters a[x]++ it should first test
> > whether a[x] is an lvalue and if yes use it accordingly.  And only if it
> > is not it should fall back to .opIndexAddButProbablyNotAssign() special
> > overloads.
> 
> Yeh, but then you lose encapsulation.  That's fine for some cases,
> like a vector, but in other cases it can be very handy to be able to
> get a notification any time one of your values changes.  You lose that
> ability once you start handing out pointers to whoever asks for one.

Your two last posts are very close to what I have on my mind myself, and 
I agree with your concerns.  The 'notification' thing is exactly what's 
missing.

My proposal is quite flexible overall in that opIndex or opIndexLvalue 
can return a struct implementing all and every op for the underlying 
type.  But it's tedious, bug-prone and not forward-compatible.

What is needed is an ability to easily wrap one type with another, 
exposing the underlying type's functionality as much as possible, but 
still receiving notifications whenever the underlying object is updated.

Now that I write this it doesn't look as smooth as I thought.  But 
anyway, here's how it works:

struct TypeWrapper(T)
{
  private T contents;
  ref T opRef() { return contents; }
  void postModify() {...}
}

Here opIndex of your sparse matrix returns this wrapper.  The wrapper 
knows whether it wraps a real element or a zero.  It can detect that a 
real element became zero and remove it from matrix.

The opRef works exactly like opCast right now in that it exposes exactly 
one underlying type.  But I hope we'll get a polymorphic opCast at some 
point so I'm using a different name for this.  The postModify is a weird 
callback which is called after the value received via opRef is modified.

Your delegate idea is great in that all the op at Assign can be replaced 
with opModify(T delegate(T) modify), giving you the ultimate 
notification.  Even more than that, the minimal type wrapper could look 
like this:

struct TypeWrapper(T)
{
  private T value;
  T opCast()
  {
    return value;
  }
  T opAssign(T v)
  {
    value = v;
    return value;
  }
  T opModify(T delegate(T v) mod)
  {
    value = mod(value);
    return value;
  }
  T opProduct(T delegate(T v) prod)
  {
    return prod(value);
  }
}

where opCast is for rvalue usage, opAssign is for pure '=', opModify is 
for '@=' and opProduct is for any regular unary or binary operators.


More information about the Digitalmars-d-announce mailing list