February 14, 2007
Andrei Alexandrescu (See Website For Email) wrote:
> Frits van Bommel wrote:
>> Andrei Alexandrescu (See Website For Email) wrote:
>>> Sean Kelly wrote:
>>>> What about:
>>>>
>>>>     foreach (i ; coll1) (j ; coll2)
>>>>     {
>>>>         if( true )
>>>>             continue i;
>>>>     }
>>>>
>>>> ie. allow 'continue' to accept labels to specify which collection is iterated.  A 'continue' without labels would iterate both.
>>>
>>> I think that's a great idea, except that "continue to label" has the same syntax: http://digitalmars.com/d/statement.html#ContinueStatement
>>
>> Does that really matter? The compiler knows whether 'i' is a label or a loop variable (presumably it can't be both at the same time?) so it knows what to do. Note that the current "continue to label" wouldn't help here since there's only one statement for a "double" iteration. So the most natural way to specify which loop to continue would be to specify the variable.
> 
> That's a good point, but changing names could complicate maintenance.
> 
>> By the way, would the new loop syntax allow more than two collections to be simultaneously iterated?
>> That would indicate the need for a "continue i, j" as well, to specify multiple variables.
>> On the other hand, with your proposed "continue foreach" clauses after the main loop that would also require an exponential number of those clauses for different sets of collections running out if you want to handle all cases...
> 
> That is correct. But it's not a real issue for a simple reason: the user would have to write the code. If they need to handle all cases, well, that's what they need to do one way or another. The foreach statement does not add anything to the equation.

Yes it does make a difference.  In the iterator case I can use all of D to determine how to resolve the fact that there are different lengths of data.  In the continue foreach scenario, I have only one recourse -- write all the continute foreach's I need.

Consider I want to add N vectors of potentially different lengths together.  With the right iterator solution I could get away with just:

foreach( iters,xs; multi_iter(a,b,c,d,e) )
  foreach(it; iters)
    if (!it.end) out[j] += xs[i];

With "continue foreach" I'm just going to give up and write a for loop.

> Of course, in most cases the user has some prior constraints on the sizes so they know which "continue foreach" sections must be written. Let me clarify that the continue foreach statements are optional, not required.

--bb
February 14, 2007
Walter Bright wrote:
> Don Clugston wrote:
>> To quote Stepanov (the link that Bill Baxter just posted):
>>
>> Alexander Stepanov Notes on Programming 10/3/2006
> 
> I still can't find the link! What is it?

http://www.stepanovpapers.com/notes.pdf

--bb
February 14, 2007
Bill Baxter wrote:
> Andrei Alexandrescu (See Website For Email) wrote:
>> Frits van Bommel wrote:
>>> Andrei Alexandrescu (See Website For Email) wrote:
>>>> Sean Kelly wrote:
>>>>> What about:
>>>>>
>>>>>     foreach (i ; coll1) (j ; coll2)
>>>>>     {
>>>>>         if( true )
>>>>>             continue i;
>>>>>     }
>>>>>
>>>>> ie. allow 'continue' to accept labels to specify which collection is iterated.  A 'continue' without labels would iterate both.
>>>>
>>>> I think that's a great idea, except that "continue to label" has the same syntax: http://digitalmars.com/d/statement.html#ContinueStatement
>>>
>>> Does that really matter? The compiler knows whether 'i' is a label or a loop variable (presumably it can't be both at the same time?) so it knows what to do. Note that the current "continue to label" wouldn't help here since there's only one statement for a "double" iteration. So the most natural way to specify which loop to continue would be to specify the variable.
>>
>> That's a good point, but changing names could complicate maintenance.
>>
>>> By the way, would the new loop syntax allow more than two collections to be simultaneously iterated?
>>> That would indicate the need for a "continue i, j" as well, to specify multiple variables.
>>> On the other hand, with your proposed "continue foreach" clauses after the main loop that would also require an exponential number of those clauses for different sets of collections running out if you want to handle all cases...
>>
>> That is correct. But it's not a real issue for a simple reason: the user would have to write the code. If they need to handle all cases, well, that's what they need to do one way or another. The foreach statement does not add anything to the equation.
> 
> Yes it does make a difference.  In the iterator case I can use all of D to determine how to resolve the fact that there are different lengths of data.  In the continue foreach scenario, I have only one recourse -- write all the continute foreach's I need.
> 
> Consider I want to add N vectors of potentially different lengths together.  With the right iterator solution I could get away with just:
> 
> foreach( iters,xs; multi_iter(a,b,c,d,e) )
>   foreach(it; iters)
>     if (!it.end) out[j] += xs[i];
> 
> With "continue foreach" I'm just going to give up and write a for loop.

That is correct. It's a limitation of "continue foreach" exactly because
it ties validity with syntactic scoping: the result gives a stronger
guarantee, but is inherently more restrictive.

I'll add that probably adding multiple vectors of different lengths would loop the other way around :o).


Andrei

February 14, 2007
Bill Baxter wrote:
> kris wrote:
> 
>> Bill Baxter wrote:
>>
>>> kris wrote:
>>>
>>>> kris wrote:
>>>>
>>>>> Bill Baxter wrote:
>>>>>
>>>>>> Frits van Bommel wrote:
>>>>>>
>>>>>>> By the way, would the new loop syntax allow more than two collections to be simultaneously iterated?
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> Whoa!  I certainly hope so.  It hadn't even occurred to me that Andrei might mean this syntax can only be used for just two collections.  If that's the case then ... ick.
>>>>>>
>>>>>> --bb
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> InterleavedIterator does multiple collections via using multiple instances of InterleavedIterator. It's simple to use, and only needs to be written once. Would be better to implement some basic iterator needs than to introduce some tricky new syntax?
>>>>>
>>>>> - Kris
>>>>
>>>>
>>>>
>>>>
>>>> Should have given an example. Simple case with 2 entities:
>>>>
>>>> auto two = InterleavedInterator (x, y);
>>>> foreach (x; two) ...
>>>>
>>>> more than 2:
>>>>
>>>> auto three = InterleavedInterator (two, z);
>>>> foreach (x; three) ...
>>>
>>>
>>>
>>> Should have also mentioned where one can find this mythical InterleavedIterator.
>>>
>>> --bb
>>
>>
>> There is no 'standard' one at this time that I know of (judging by the discussion on it a while back). However, Tango does have this beastie in the collections package. The point is, coming up with a lightweight core Iterator approach would likely provide a simpler and more dependable solution.
> 
> 
> Ok that wasn't clear to me.  It sounded like you were talking about code I could type in today and have it work given suitable (but not specified) imports.
> 
>> in the above example, x, y, and z are all iterators themselves. If D had a core notion of Iterator, that's what those would be. For instance, D iterators might map to a delegate (which is what the body of a foreach actually is).
> 
> 
> Yeh, basically it's the same as the Python izip that mentioned.  That's python's name for InterleavedIterator.
> 
> I think the issue with D right now is that the 'x' returned by a hypothetical InterleavedIterator would ideally be a tuple.  And you would access the elements with x[0],x[1],x[2] (int the 'three' case above).  Or you could do  foreach(x,y,z; three) and have it unpacked for you.
> 
> I think it would be great if this kind of stuff worked.  I'm much less excited about a built-in syntax that _only_ knows how to do that one trick.
> 
> --bb


Why would it return a tuple? Would the collection content be of differing types? If not, then the InterleavedIterator would likely have an opApply() for use in the foreach? That's how the Tango one operates, fwiw.
February 14, 2007
Andrei Alexandrescu (See Website For Email) wrote:
> Bill Baxter wrote:
> 
>> kris wrote:
>>
>>> kris wrote:
>>>
>>>> Bill Baxter wrote:
>>>>
>>>>> Frits van Bommel wrote:
>>>>>
>>>>>> By the way, would the new loop syntax allow more than two collections to be simultaneously iterated?
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> Whoa!  I certainly hope so.  It hadn't even occurred to me that Andrei might mean this syntax can only be used for just two collections.  If that's the case then ... ick.
>>>>>
>>>>> --bb
>>>>
>>>>
>>>>
>>>> InterleavedIterator does multiple collections via using multiple instances of InterleavedIterator. It's simple to use, and only needs to be written once. Would be better to implement some basic iterator needs than to introduce some tricky new syntax?
>>>>
>>>> - Kris
>>>
>>>
>>>
>>> Should have given an example. Simple case with 2 entities:
>>>
>>> auto two = InterleavedInterator (x, y);
>>> foreach (x; two) ...
>>>
>>> more than 2:
>>>
>>> auto three = InterleavedInterator (two, z);
>>> foreach (x; three) ...
>>
>>
>> Should have also mentioned where one can find this mythical InterleavedIterator.
> 
> 
> The issue with such a multi-iterator is that it makes it easier to make errors, and harder to write efficient and correct code that's statically verifiable.
> 
> I'm not sure when the interleaved iterator stops iterating, but there are two possibilities, none of which is satisfactory:
> 
> 1. Stop after the shortest of the two collections is done. Then user code must query the state of the iterator after the loop to figure what extra work is to be done:
> 
> auto two = InterleavedInterator (x, y);
> foreach (x; two) { ... }
> if (two.MoreData(0)) {
>   auto back2one = two.Project(0); // fetch the first iterator
>   foreach (x ; back2one) { ... }
> } else if (two.moreData(1)) {
>   ... same (or)deal ...
> }
> 
> This is way more work than there should.
> 
> 2. Stop after the longest of the two collections is done. Then user code must ensure _at each step_ that both iterators have meaningful data:
> 
> auto two = InterleavedInterator (x, y);
> foreach (x; two) {
>   if (two.HasData(0)) { ... }
>   else { ... only the second iter has data ... }
> }
> 
> This is unclear, verbose, and probably suboptimal.
> 
> The scoping of foreach links the scope of the variables with their validity range, which rules out a class of possible errors entirely:
> 
> foreach (x ; c1) (y ; c2) (z ; c3) {
>   ... x, y, z syntactically accessible _and_ valid ...
> }
> continue foreach (x, z) {
>   ... x is both invalid _and_ syntactically inaccessible ...
> }
> 
> As I mentioned in a different post, the fact that there are combinatorial potential sub-foreach statements is a non-issue.
> 
> 
> Andrei

If x, y, & z are of differing type, then I'd agree.
February 14, 2007
Andrei Alexandrescu (See Website For Email) wrote:
> Bill Baxter wrote:
>> Andrei Alexandrescu (See Website For Email) wrote:
>>> Frits van Bommel wrote:
>>>> Andrei Alexandrescu (See Website For Email) wrote:
>>>>> Sean Kelly wrote:
>>>>>> What about:
>>>>>>
>>>>>>     foreach (i ; coll1) (j ; coll2)
>>>>>>     {
>>>>>>         if( true )
>>>>>>             continue i;
>>>>>>     }
>>>>>>
>>>>>> ie. allow 'continue' to accept labels to specify which collection is iterated.  A 'continue' without labels would iterate both.
>>>>>
>>>>> I think that's a great idea, except that "continue to label" has the same syntax: http://digitalmars.com/d/statement.html#ContinueStatement
>>>>
>>>> Does that really matter? The compiler knows whether 'i' is a label or a loop variable (presumably it can't be both at the same time?) so it knows what to do. Note that the current "continue to label" wouldn't help here since there's only one statement for a "double" iteration. So the most natural way to specify which loop to continue would be to specify the variable.
>>>
>>> That's a good point, but changing names could complicate maintenance.
>>>
>>>> By the way, would the new loop syntax allow more than two collections to be simultaneously iterated?
>>>> That would indicate the need for a "continue i, j" as well, to specify multiple variables.
>>>> On the other hand, with your proposed "continue foreach" clauses after the main loop that would also require an exponential number of those clauses for different sets of collections running out if you want to handle all cases...
>>>
>>> That is correct. But it's not a real issue for a simple reason: the user would have to write the code. If they need to handle all cases, well, that's what they need to do one way or another. The foreach statement does not add anything to the equation.
>>
>> Yes it does make a difference.  In the iterator case I can use all of D to determine how to resolve the fact that there are different lengths of data.  In the continue foreach scenario, I have only one recourse -- write all the continute foreach's I need.
>>
>> Consider I want to add N vectors of potentially different lengths together.  With the right iterator solution I could get away with just:
>>
>> foreach( iters,xs; multi_iter(a,b,c,d,e) )
>>   foreach(it; iters)
>>     if (!it.end) out[j] += xs[i];
>>
>> With "continue foreach" I'm just going to give up and write a for loop.
> 
> That is correct. It's a limitation of "continue foreach" exactly because
> it ties validity with syntactic scoping: the result gives a stronger
> guarantee, but is inherently more restrictive.

Ok so it's like more of the same foreach business.  If it works for you, great.  If not, sorry.  Go find another way to do it.  Meh.

> I'll add that probably adding multiple vectors of different lengths would loop the other way around :o).

Yeh, fair enough.  :-)
Let's say I want to print out the data columnwise instead.

foreach( iterset; multi_iter(a,b,c,d,e) ) {
  foreach(i,it; iterset)
    writef("%s ", it.end ? "<no data>" : it.val);
  writefln();
}

I will give you though, that it would be more difficult to make that as efficient as the more restricted solution you're talking about.

--bb
February 14, 2007
kris wrote:
> Bill Baxter wrote:
>> kris wrote:
>>
>>> Bill Baxter wrote:
>>>
>>>> kris wrote:
>>>>
>>>>> kris wrote:
>>>>>
>>>>>> Bill Baxter wrote:
>>>>>>
>>>>>>> Frits van Bommel wrote:
>>>>>>>
>>>>>>>> By the way, would the new loop syntax allow more than two collections to be simultaneously iterated?
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> Whoa!  I certainly hope so.  It hadn't even occurred to me that Andrei might mean this syntax can only be used for just two collections.  If that's the case then ... ick.
>>>>>>>
>>>>>>> --bb
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> InterleavedIterator does multiple collections via using multiple instances of InterleavedIterator. It's simple to use, and only needs to be written once. Would be better to implement some basic iterator needs than to introduce some tricky new syntax?
>>>>>>
>>>>>> - Kris
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> Should have given an example. Simple case with 2 entities:
>>>>>
>>>>> auto two = InterleavedInterator (x, y);
>>>>> foreach (x; two) ...
>>>>>
>>>>> more than 2:
>>>>>
>>>>> auto three = InterleavedInterator (two, z);
>>>>> foreach (x; three) ...
>>>>
>>>>
>>>>
>>>> Should have also mentioned where one can find this mythical InterleavedIterator.
>>>>
>>>> --bb
>>>
>>>
>>> There is no 'standard' one at this time that I know of (judging by the discussion on it a while back). However, Tango does have this beastie in the collections package. The point is, coming up with a lightweight core Iterator approach would likely provide a simpler and more dependable solution.
>>
>>
>> Ok that wasn't clear to me.  It sounded like you were talking about code I could type in today and have it work given suitable (but not specified) imports.
>>
>>> in the above example, x, y, and z are all iterators themselves. If D had a core notion of Iterator, that's what those would be. For instance, D iterators might map to a delegate (which is what the body of a foreach actually is).
>>
>>
>> Yeh, basically it's the same as the Python izip that mentioned.  That's python's name for InterleavedIterator.
>>
>> I think the issue with D right now is that the 'x' returned by a hypothetical InterleavedIterator would ideally be a tuple.  And you would access the elements with x[0],x[1],x[2] (int the 'three' case above).  Or you could do  foreach(x,y,z; three) and have it unpacked for you.
>>
>> I think it would be great if this kind of stuff worked.  I'm much less excited about a built-in syntax that _only_ knows how to do that one trick.
>>
>> --bb
> 
> 
> Why would it return a tuple? Would the collection content be of differing types? If not, then the InterleavedIterator would likely have an opApply() for use in the foreach? That's how the Tango one operates, fwiw.


I must not understand what your InterleavedIterator does then.  I'm thinking of something like:
char[][] names = ["chuck", "barney", "bart"];
int[] ids = [12983, 32345, 39284];

foreach (x; InterleavedIterator(names,ids)) {
   writefln("Name=%s id=%s", x[0], x[1]);
}

--bb
February 14, 2007
kris wrote:
> Andrei Alexandrescu (See Website For Email) wrote:
>> Bill Baxter wrote:
>>
>>> kris wrote:
>>>
>>>> kris wrote:
>>>>
>>>>> Bill Baxter wrote:
>>>>>
>>>>>> Frits van Bommel wrote:
>>>>>>
>>>>>>> By the way, would the new loop syntax allow more than two collections to be simultaneously iterated?
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> Whoa!  I certainly hope so.  It hadn't even occurred to me that Andrei might mean this syntax can only be used for just two collections.  If that's the case then ... ick.
>>>>>>
>>>>>> --bb
>>>>>
>>>>>
>>>>>
>>>>> InterleavedIterator does multiple collections via using multiple instances of InterleavedIterator. It's simple to use, and only needs to be written once. Would be better to implement some basic iterator needs than to introduce some tricky new syntax?
>>>>>
>>>>> - Kris
>>>>
>>>>
>>>>
>>>> Should have given an example. Simple case with 2 entities:
>>>>
>>>> auto two = InterleavedInterator (x, y);
>>>> foreach (x; two) ...
>>>>
>>>> more than 2:
>>>>
>>>> auto three = InterleavedInterator (two, z);
>>>> foreach (x; three) ...
>>>
>>>
>>> Should have also mentioned where one can find this mythical InterleavedIterator.
>>
>>
>> The issue with such a multi-iterator is that it makes it easier to make errors, and harder to write efficient and correct code that's statically verifiable.
>>
>> I'm not sure when the interleaved iterator stops iterating, but there are two possibilities, none of which is satisfactory:
>>
>> 1. Stop after the shortest of the two collections is done. Then user code must query the state of the iterator after the loop to figure what extra work is to be done:
>>
>> auto two = InterleavedInterator (x, y);
>> foreach (x; two) { ... }
>> if (two.MoreData(0)) {
>>   auto back2one = two.Project(0); // fetch the first iterator
>>   foreach (x ; back2one) { ... }
>> } else if (two.moreData(1)) {
>>   ... same (or)deal ...
>> }
>>
>> This is way more work than there should.
>>
>> 2. Stop after the longest of the two collections is done. Then user code must ensure _at each step_ that both iterators have meaningful data:
>>
>> auto two = InterleavedInterator (x, y);
>> foreach (x; two) {
>>   if (two.HasData(0)) { ... }
>>   else { ... only the second iter has data ... }
>> }
>>
>> This is unclear, verbose, and probably suboptimal.
>>
>> The scoping of foreach links the scope of the variables with their validity range, which rules out a class of possible errors entirely:
>>
>> foreach (x ; c1) (y ; c2) (z ; c3) {
>>   ... x, y, z syntactically accessible _and_ valid ...
>> }
>> continue foreach (x, z) {
>>   ... x is both invalid _and_ syntactically inaccessible ...
>> }
>>
>> As I mentioned in a different post, the fact that there are combinatorial potential sub-foreach statements is a non-issue.
>>
>>
>> Andrei
> 
> If x, y, & z are of differing type, then I'd agree.

If they are of the same type and in an arbitrarily large numbers (x1, x2, x3...), we start talking about cutting through some sort of a matrix or manifold, which is an entirely different business.

Anyhow, I think it's clear by now that the language makes some idioms faster, and library iterators make some other idioms faster. Clearly library iterators are useful. The question is if the language-helped idioms are encountered often enough to justify the cognitive load of implementing them.

I'm biased by my own C++ codebase, which does a _ton_ of looping (linear algebra, neural nets, manifold learning...) I have a nice FOREACH(i, 0, n) macro that takes care very effectively of most loops, with proper type deduction (gotta love gcc's typeof), limit hoisting, you name it. (I've sat down and measured that it has no impact on the efficiency of the generated code, which is paramount.)

In contrast, the few places in which I had to use a straight for loop or maintain extra variables to do parallel iterations really make the thin facade break down as all of a sudden I need to fully explain myself to the compiler. All of the extra cases could be helped, and efficiently, by continue foreach (actually, to tell the truth, I could also use the foreach_reverse/continue foreach_reverse correspondent feature).


Andrei

February 14, 2007
Bill Baxter wrote:
> Andrei Alexandrescu (See Website For Email) wrote:
>> Bill Baxter wrote:
>>> Andrei Alexandrescu (See Website For Email) wrote:
>>>> Frits van Bommel wrote:
>>>>> Andrei Alexandrescu (See Website For Email) wrote:
>>>>>> Sean Kelly wrote:
>>>>>>> What about:
>>>>>>>
>>>>>>>     foreach (i ; coll1) (j ; coll2)
>>>>>>>     {
>>>>>>>         if( true )
>>>>>>>             continue i;
>>>>>>>     }
>>>>>>>
>>>>>>> ie. allow 'continue' to accept labels to specify which collection is iterated.  A 'continue' without labels would iterate both.
>>>>>>
>>>>>> I think that's a great idea, except that "continue to label" has the same syntax: http://digitalmars.com/d/statement.html#ContinueStatement
>>>>>
>>>>> Does that really matter? The compiler knows whether 'i' is a label or a loop variable (presumably it can't be both at the same time?) so it knows what to do. Note that the current "continue to label" wouldn't help here since there's only one statement for a "double" iteration. So the most natural way to specify which loop to continue would be to specify the variable.
>>>>
>>>> That's a good point, but changing names could complicate maintenance.
>>>>
>>>>> By the way, would the new loop syntax allow more than two collections to be simultaneously iterated?
>>>>> That would indicate the need for a "continue i, j" as well, to specify multiple variables.
>>>>> On the other hand, with your proposed "continue foreach" clauses after the main loop that would also require an exponential number of those clauses for different sets of collections running out if you want to handle all cases...
>>>>
>>>> That is correct. But it's not a real issue for a simple reason: the user would have to write the code. If they need to handle all cases, well, that's what they need to do one way or another. The foreach statement does not add anything to the equation.
>>>
>>> Yes it does make a difference.  In the iterator case I can use all of D to determine how to resolve the fact that there are different lengths of data.  In the continue foreach scenario, I have only one recourse -- write all the continute foreach's I need.
>>>
>>> Consider I want to add N vectors of potentially different lengths together.  With the right iterator solution I could get away with just:
>>>
>>> foreach( iters,xs; multi_iter(a,b,c,d,e) )
>>>   foreach(it; iters)
>>>     if (!it.end) out[j] += xs[i];
>>>
>>> With "continue foreach" I'm just going to give up and write a for loop.
>>
>> That is correct. It's a limitation of "continue foreach" exactly because
>> it ties validity with syntactic scoping: the result gives a stronger
>> guarantee, but is inherently more restrictive.
> 
> Ok so it's like more of the same foreach business.  If it works for you, great.  If not, sorry.  Go find another way to do it.  Meh.

This goes for many core features. Language design, exactly like code generation optimization, is more about finding the right tradeoff instead of the 100% adobe. If foreach (i ; c) takes care of 70% of cases, and foreach (i ; c) (j ; d) brings that to 90%, that's probably better than a one-size-fits-all solution that boasts uniform clunkiness for 100% of the cases.


Andrei
February 14, 2007
Andrei Alexandrescu (See Website For Email) wrote:
> kris wrote:
> 
>> Andrei Alexandrescu (See Website For Email) wrote:
>>
>>> Bill Baxter wrote:
>>>
>>>> kris wrote:
>>>>
>>>>> kris wrote:
>>>>>
>>>>>> Bill Baxter wrote:
>>>>>>
>>>>>>> Frits van Bommel wrote:
>>>>>>>
>>>>>>>> By the way, would the new loop syntax allow more than two collections to be simultaneously iterated?
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> Whoa!  I certainly hope so.  It hadn't even occurred to me that Andrei might mean this syntax can only be used for just two collections.  If that's the case then ... ick.
>>>>>>>
>>>>>>> --bb
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> InterleavedIterator does multiple collections via using multiple instances of InterleavedIterator. It's simple to use, and only needs to be written once. Would be better to implement some basic iterator needs than to introduce some tricky new syntax?
>>>>>>
>>>>>> - Kris
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> Should have given an example. Simple case with 2 entities:
>>>>>
>>>>> auto two = InterleavedInterator (x, y);
>>>>> foreach (x; two) ...
>>>>>
>>>>> more than 2:
>>>>>
>>>>> auto three = InterleavedInterator (two, z);
>>>>> foreach (x; three) ...
>>>>
>>>>
>>>>
>>>> Should have also mentioned where one can find this mythical InterleavedIterator.
>>>
>>>
>>>
>>> The issue with such a multi-iterator is that it makes it easier to make errors, and harder to write efficient and correct code that's statically verifiable.
>>>
>>> I'm not sure when the interleaved iterator stops iterating, but there are two possibilities, none of which is satisfactory:
>>>
>>> 1. Stop after the shortest of the two collections is done. Then user code must query the state of the iterator after the loop to figure what extra work is to be done:
>>>
>>> auto two = InterleavedInterator (x, y);
>>> foreach (x; two) { ... }
>>> if (two.MoreData(0)) {
>>>   auto back2one = two.Project(0); // fetch the first iterator
>>>   foreach (x ; back2one) { ... }
>>> } else if (two.moreData(1)) {
>>>   ... same (or)deal ...
>>> }
>>>
>>> This is way more work than there should.
>>>
>>> 2. Stop after the longest of the two collections is done. Then user code must ensure _at each step_ that both iterators have meaningful data:
>>>
>>> auto two = InterleavedInterator (x, y);
>>> foreach (x; two) {
>>>   if (two.HasData(0)) { ... }
>>>   else { ... only the second iter has data ... }
>>> }
>>>
>>> This is unclear, verbose, and probably suboptimal.
>>>
>>> The scoping of foreach links the scope of the variables with their validity range, which rules out a class of possible errors entirely:
>>>
>>> foreach (x ; c1) (y ; c2) (z ; c3) {
>>>   ... x, y, z syntactically accessible _and_ valid ...
>>> }
>>> continue foreach (x, z) {
>>>   ... x is both invalid _and_ syntactically inaccessible ...
>>> }
>>>
>>> As I mentioned in a different post, the fact that there are combinatorial potential sub-foreach statements is a non-issue.
>>>
>>>
>>> Andrei
>>
>>
>> If x, y, & z are of differing type, then I'd agree.
> 
> 
> If they are of the same type and in an arbitrarily large numbers (x1, x2, x3...), we start talking about cutting through some sort of a matrix or manifold, which is an entirely different business.

So if we did have a language-based iterator (which was a hot topic recently), it might take the form of a generator?

[snip]