Bug 5605 – [tdpl] foreach with ranges doesn't support opSlice()
Status
RESOLVED
Resolution
FIXED
Severity
normal
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2011-02-17T14:19:00Z
Last change time
2011-12-25T15:16:31Z
Keywords
patch, rejects-valid
Assigned to
nobody
Creator
acehreli
Comments
Comment #0 by acehreli — 2011-02-17T14:19:37Z
TDPL mentions a very useful feature on page 381 under "12.9.1 foreach with Iteration Primitives".
(Note: I am copying all of this manually; the typos are mine):
It first shows a function that includes a possible "compiler rewrite" of a foreach loop:
void process(SimpleList!int lst) {
for (auto __c = lst; !__c.empty; __c.popFront()) {
auto value = __c.front;
... // Use value of type int
}
}
It then says
<quote>
.... if the iterated object offers the slice operator with no arguments lst[], __c is initialized with lst[] instead of lst. This is in order to allow "extracting" the iteration means out of a container without requiring the container to define the three iteration primitives.
</quote>
I couldn't get that to work with the following code:
import std.stdio;
struct MyRange
{
int theOnlyOne;
@property bool empty() const
{
return false;
}
@property ref int front()
{
return theOnlyOne;
}
void popFront()
{}
}
struct MyCollection
{
MyRange opSlice() const
{
return MyRange();
}
}
void main()
{
auto coll = MyCollection();
foreach (i; coll) { // <-- compilation error
// ...
}
}
Error: cannot infer type for i
Providing the type of i as 'int' or 'ref int' produce a different error:
foreach (int i; coll) {
or
foreach (ref int i; coll) {
produce
Error: no property 'opApply' for type 'MyCollection'
Please note that taking a slice explicitly works:
foreach (i; coll[]) {
but not the feature mentioned in TDPL.
Thank you,
Ali
Comment #1 by schveiguy — 2011-02-18T04:20:34Z
Adding [tdpl] for better searching
Comment #2 by denis.spir — 2011-03-05T03:39:11Z
(In reply to comment #0)
> TDPL mentions a very useful feature on page 381 under "12.9.1 foreach with
> Iteration Primitives".
> [...]
> It then says
>
> <quote>
> .... if the iterated object offers the slice operator with no arguments lst[],
> __c is initialized with lst[] instead of lst. This is in order to allow
> "extracting" the iteration means out of a container without requiring the
> container to define the three iteration primitives.
> </quote>
>
> I couldn't get that to work with the following code:
> [...]
Yes, I tried to use this feature as well, found it cool.
But I guess it misses the point, in fact. The core "detail" is the (intended, but not clear at all imo) distinction between collections/containers as data structures (conceptually) holding elements, on one hand, and ranges (iterators) on the other, aimed to offer "views" allowing iteration over their elements.
While not necessarily a frequent need, this scheme offers the possibility to define multiple views, and even multiple times the same view in parallel, on a given collection.
The problem is that, since we do not iterate anymore over a container, but over one view of it, we thus thus need a way to specify this view:
foreach (element ; range(collection)) {...}
The advantage of this explicite form is that it is uniform, and allows specifying /which/ view when appropriate:
foreach (element ; reverse(collection)) {...}
This is similar to Lua's approach:
for k,v in pairs(table) do ... end
for k in keys(table) do ... end
We could have a 'range' builtin function returning the default range for each builtin type. (The term 'range' is just here a name illustrating the idea.) Or better, a property, allowing its definition for custom collections. Thus, iterating over any conformant collection, according to (whatever is supposed to be) the standard view, would always be written using 'range':
foreach (element; container.range) {...}
But then, we have to write down ".range" everywhere, while in nearly all cases the view via which we want iterate over a collection is the standard one. But: iteration is a language feature realised via the 'foreach' control statement. Thus, why not have an associated "language method", like 'opRange'? Its semantics beeing to return the standard iteration view of a collection type, whenever the programmer does not specify any given one. Then, we could write:
foreach (element; container) {...}
for any conformant container type.
This would also finally solve a latent conflict between people stating ranges should always be external structures to collections, as intended I guess, and ones (including myself) who like to implement them on container types directly, if only to avoid code noise.
Denis
PS: I just realise this proposal would be the D equivalent of python's __iter__ (not inventing the wheel ;-)