opNext: Simplified ranges
Tomer at Weka
Tomer at Weka
Sat Nov 5 06:01:54 UTC 2022
I find the empty/front/popNext protocol very cumbersome to
implement for most cases (at least in the Weka codebase), which
causes people to just prefer opApply (even though it has its own
set of issues).
Today I even ran into a problem that cannot be solved using the
existing protocol: a consuming range (think a file stream) that
should not consume the element if the foreach has been broken.
Using opApply, this is easy (the delegate returns nonzero), but
using popNext it's next to impossible (or at least really really
cumbersome). I don't want to go into all the details, so let's
just jump to the suggested protocol.
```
bool opNext(out T elem) {...}
```
`opNext` returns true if it "produced" an element, false if it
reached the end. That way
```
foreach (elem; range) {
...
}
```
Simply gets lowered to
```
T elem;
while (range.opNext(elem)) {
...
}
```
Of course this protocol can live side-by-side with the existing
protocol, just take precedence in the lowering rules. A concrete
example:
```
struct LinkedList {
int value;
LinkedList* next;
struct Range {
LinkedList* cursor;
bool opNext(out int val) nothrow @nogc {
if (cursor is null) {
return false;
}
val = cursor.value;
return true;
}
}
auto members() {return Range(&this);}
}
foreach (val; myList.members) {
...
}
```
Notes:
* `save()` can work for the new protocol as well, it just needs
to duplicate the iterator's state
* Maybe it would be wise to complement this with opPrev for
bidirectional ranges, but I have to say I've rarely seen any use
of them
* I think chaining with map/filter/etc. is even easier to
implement in the new protocol
More information about the Digitalmars-d
mailing list