Can we get rid of opApply?

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Mon Jan 19 20:38:01 PST 2009


Steven Schveighoffer wrote:
> "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.

I agree.

> 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.

Just that an array would be nicer than numbered properties.

>>> 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.

No delegate, just something like:

auto addr = &(__r.head);

then replace *addr throughout whenever the element is being used.

>>> 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...

I hear ya. Please note that as far as this extension goes I'm just 
making things up as I go while juggling one one-month-old kid on one 
hand, two visiting parents on another hand, and one thesis on yet 
another hand (paper accepted today... yay!).

It would be great if you and/or others came with good proposals on how 
to make ranges work well. For now, ranges only allow one iterated object 
and one type, and I agree that that's a step backwards from opApply's 
nice behavior. So far ref parameters and tuples have been mentioned. 
Both have merit, and ref parameters have the additional advantage that 
overloading and type deduction works with them.


Andrei



More information about the Digitalmars-d mailing list