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