On 6/13/17 1:15 PM, Luís Marques wrote:
In the documentation for foreach with range concepts (not x..y ranges)
(<http://dlang.org/spec/statement.html#foreach-with-ranges>) I did not
find anything about foreach supporting iteration indices/tuple unpacking
for ranges, such as `foreach(i, elm; range)`. It is "documented" in an
example for std.range.enumerate though [1].

I found a bug report asking for that to be documented [2] and one saying
that it should be removed [3].

One of the things that I like in D is the plasticity of the code. In the
situation that brought me to this issue, I started with a plain slice
which I iterated with foreach(i, foo; foos). Then `foos` became a range,
and the foreach became foreach(i, foo; foos.enumerate); Thus, I didn't
have to restructure my foreach iteration, moving the index variable
outside the foreach statement, and manually incrementing the index,
making this change less disruptive.

Some thoughts about this:

- It doesn't strike me as very realistic to remove support for
foreach(i, e; range.enumerate) now, as probably a lot of people are
relying on this. I find it very useful, especially as currently there
doesn't seem to be a good alternative.

We aren't going to as far as I can tell. I closed that bug report.

- The plasticity was not perfect in my example; ideally I wouldn't have
to change the foreach statement at all (i.e., add ..enumerate).
Consider: my range had random access, why couldn't foreach itself
generate the index, as it does for slices, instead of relying on
.enumerate and tuple unpacking? Maybe it would even make sense for input
ranges, I'm not sure.

The issue here is the difference between foreach on arrays (which has nothing to do with ranges), and foreach on a range. In the former, the array index is wholly a concept of the foreach loop itself. The compiler inserts and maintains the index in the loop, as the array structure has no storage or maintenance of an index.

In the latter, the index is maintained by the range, and so stored there. AND it's copied and stored as a temporary for your loop via the unpacking of the tuple.

I feel a change in the way foreach and ranges interact may be needed to make things more efficient and less awkward. opApply has some really nice features, one of them being that you can declare loop-specific data such as an index, and another being that foreach'ing an opApply structure with different types/numbers of parameters works just fine.

But I think leaving the definition of the index up to the range itself is paramount -- I don't want every range to be able to have a size_t index, as that's not always what you want, and it conflicts with other items. What we may need is a smarter way to get from the type parameters at the front of the foreach to an iterable type.

- Since foreach with ranges and unpacking (i.e., indices when using
.enumerate) seems here to stay for now, please document it. The code I
was writing was supporting material for an article. But I'm afraid to
advocate an approach which relies on behavior which is undocumented and
which people are arguing should be removed. Not exactly confidence
inspiring...

It should be documented, 100%.

-Steve

Reply via email to