Can we get rid of opApply?

Steven Schveighoffer schveiguy at yahoo.com
Mon Jan 19 12:19:32 PST 2009


When looking at ranges, it seems like a much better model for iteration than 
opApply.  It seems like it could be a whole replacement for opApply.  But 
when looking at the current usage of opApply, there are some holes.

foreach(i, x; range)
{
}

What exactly happens?  Do you have to return a tuple from a range with 
head()?

Also, another thing that was nice about opApply, if you had multiple ways to 
iterate over an aggregate, you simply altered your arguments to foreach. 
How can this be implemented with ranges?

class Aggregate
{
   int opApply(int delegate(ref int idx, ref int value) dg) {...}
   int opApply(int delegate(ref int value) dg) {...}
}

Aggregate A = new Aggregate;
foreach(i, ref x; A) {...}
foreach(x; A) {...}

Maybe ranges need some more functionality to do this.  I can't see how to do 
it with Tuples, as you'd have to be able to overload head() based on return 
value.  Or if a proposed opRange is supported from Aggregate (opRange is 
called if it exists, otherwise if the aggregate is a range use that, 
otherwise look for opApply), you'd have to overload the range type returned 
based on usage.

The only thing I could think of is to change head and toe to take reference 
parameters instead of using the return value, although that's ugly.  Maybe 
it could be supported in addition to returning a value?

e.g.:

struct R
{
   int head() { ...}
   void head(int *i, int *x) {...}
   void head(int *i, int **x) { ... }
}

foo(R r)
{
   foreach(x; r)
   {
   }
   foreach(i, x; r)
   {
   }
   foreach(i, ref x; r)
   {
   }
}

// foo translates to:

foo(R r)
{
   for(auto __r = r; !__r.empty(); __r.next())
   {
       auto x = __r.head();
   }
   for(auto __r = r; !__r.empty(); __r.next())
   {
       int i, x; // compiler figures out from signature of head()
       __r.head(&i, &x);
   }
   for(auto __r = r; !__r.empty(); __r.next())
   {
      int i; // compiler figures out from signature of head()
      int *__x; // only here for explanation, compiler would optimize this 
extra variable out.
      __r.head(&i, &x);
      ref int x = *__x; // ditto
   }
}

This is ugly, and requires some compiler magic, but do you see another way 
to do it?  I didn't know if "ref ref" would work, so that's why I use 
pointers instead.  Since ref is a storage class, I don't know if the 
compiler would overload anyways...

Even with all this, I still think it's prettier than opApply with the 
delegate signature :)  And probably it performs better.

-Steve 





More information about the Digitalmars-d mailing list