Can we get rid of opApply?

Steven Schveighoffer schveiguy at yahoo.com
Mon Jan 19 13:17:06 PST 2009


"Andrei Alexandrescu" wrote
> Steven Schveighoffer wrote:
>> 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()?
>
> One possibility I'd discussed with Walter is that for the usage above, the 
> compiler asks for the .key property in addition to .head. If more keys are 
> specified, .key1, .key2... are required.

I'd say labeling them "key" would be slightly biased.  The arguments to 
opApply might not always be considered a key.  I'd label them .head1, 
.head2, etc.

Also, how do you recreate this case:

struct S
{
   int opApply(int delegate(ref int idx, ref int v) dg) {...}
   int opApply(int delegate(ref string k, ref int v) dg) {...}
   int opApply(int delegate(ref int idx, ref string k, ref int v) dg) {...}
}

Think of S as a container that can look up values by index or by string 
(like a sorted dictionary).

> Probably an array would be a nicer solution.

I'm interested to hear what you mean by that.

>> 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) {...}
>
> Keys will take care of that too. The "ref" thing will generate different 
> code by taking the address of head (not sure if Walter implemented that).

You mean taking the address of the return value from head?  Or using a 
delegate?  I'm not sure what you mean...  I'd be wary of performance if you 
mean take a delegate.

>
>> 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.
>
> Yes, I'm afraid type deduction will be harmed with ranges.

BAD BAD BAD!  I love being able to do type deduction in foreach...  Not 
having that would be a real hard pill to swallow...

>
>> 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?
>
> [snip]
>
>> 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...
>
> One simple solution to the overloading by return type would be to have 
> head be a template. Then if you say:
>
> foreach (int e; range) {}
>
> the corresponding assignment for e will be:
>
> int e = __r.head!(int)();
>
> There are a few more wrinkles to fill with Botox though :o).

Yuck.  I'd much rather see this implemented with ref parameters.  It seems 
like a waste to have to use templates for overloading in my range struct 
when a perfectly good type system is available.  Just my opinion.

-Steve 





More information about the Digitalmars-d mailing list