foreach range with index

Steven Schveighoffer via Digitalmars-d digitalmars-d at puremagic.com
Wed Jun 14 09:17:50 PDT 2017


On 6/14/17 11:42 AM, Luís Marques wrote:
> On Wednesday, 14 June 2017 at 14:35:32 UTC, Steven Schveighoffer wrote:
>> What I'd rather see is a better mechanism for ranges (or things that
>> provide ranges) to hook the foreach syntax to allow better control and
>> power from ranges without using traditional opApply.
>>
>> For example, if we had:
>>
>> foreach(size_t a, b; x)
>>
>> translates to:
>>
>> for(auto _context = x.opApply!(size_t, void); !context.empty;
>> _context.popFront)
>> {
>>    auto (a, b) = _context.front; // the way it works now with tuples,
>> don't think there's a real syntax for this.
>>    ...
>> }
>
> An approach like that seems fine to me. In any case, why do it in
> exclusion of a foreach-generated index? Imagine that I define an input
> range. Can we assume that range.front is *conceptually* equivalent to
> range[0], even if the range doesn't define random access?  Because if we
> can, then, if the range doesn't define the new opApply or whatever, why
> not allow foreach to generate the index?

That's when you use range.enumerate ;)

>> Yes, foreach could be augmented to do that. However, this is
>> essentially a way to implement enumerate without the .enumerate, and
>> nothing more. It's not a lot of added benefit IMO.
>
> Consider this case. You create a prototype where foo is a slice. Then
> you start improving the code and foo becomes a range. In your code you
> have several places that use foo with foreach, and several places where
> it is used directly. If you define the new foo as newRangeFoo.enumerate,
> then the several foreach work automatically, but the other uses don't
> because they have to be changed to foo.value; if you do it the other way
> around, the direct uses work automatically but the foreach all have to
> be changed from foo to foo.enumerate. Thus you have lost a lot of the
> code plasticity. If the foreach statement generated the indices then it
> would work automatically.

This is inherent of arrays working differently than ranges, but being 
considered ranges via std.array. People consider slices to be ranges, 
but they really aren't as far as the compiler is concerned.

I don't know if there is room to allow range-defined indexes and 
foreach-defined indexes. foreach(a, b; someRange) has to do one or the 
other. Otherwise, changes to how someRange operates can make this a 
completely different operation.

> This could also be solved by the range defining your new version of
> opApply and foreach understanding that. Assuming that we can wrap old
> ranges (e.g. from a library you don't want to change) with that opApply,
> I would be fine with that solution as long as this is something that
> actually goes forward. I'm just afraid this is something that will
> linger because it's a more difficult design decision, while having
> foreach support index generation seems like a more self contained
> solution that could be agreed upon and implemented quickly.

I think opApply is kind of a forgotten piece of the language since 
ranges have been introduced. We could resurrect it quite easily this 
way, as opApply with template parameters that return another range does 
not conflict with opApply that takes a delegate. Easy to see if one 
works and if not try something else. The huge benefit of this mechanism 
is to allow complete control to the type over how it should be iterated, 
and not have to fight the compiler.

-Steve


More information about the Digitalmars-d mailing list