Skip to content

Commit 99dbd24

Browse files
committed
update readme to show new iteration protocol
1 parent b35ee81 commit 99dbd24

1 file changed

Lines changed: 55 additions & 19 deletions

File tree

README.md

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -353,35 +353,71 @@ Standard library documentation lives in the source code. The generated markdown
353353
file can be found
354354
[here](https://github.com/p7g/c-bytecode-vm/blob/master/docs/stdlib.md).
355355

356-
Here are some idioms that have appeared in the standard library:
356+
Here are some patterns that have appeared in the standard library:
357357

358358
### Iteration
359359

360-
This language has no generators, nor any built-in iteration protocol. The way
361-
this is done in the standard library (see the [iter] module) is using closures.
360+
Using [traits], the [iter] module defines an iteration protocol. This protocol
361+
involves 2 traits: `Iterable` and `Iterator`.
362362

363-
[iter]: https://github.com/p7g/c-bytecode-vm/blob/main/lib/iter.cb
363+
[traits]: /blob/main/lib/trait.cb
364+
[iter]: /blob/main/lib/iter.cb
364365

365-
This is easiest to illustrate with an example:
366-
```js
367-
function range(to) {
368-
let i = 0;
366+
The `Iterable` trait makes an object iterable (as the name suggests). It has a
367+
single method called `iter(self)` that must return an `Iterator`. Objects of any
368+
type that implements this trait can be passed as the iterable argument to any
369+
function in the `iter` module.
370+
371+
An `Iterator` object is a stateful iterator with a `next(self)` parameter, which
372+
must return the next item in the sequence.
373+
374+
Here's an example of both:
375+
376+
```c++
377+
struct Range { start, stop, step }
378+
struct RangeIterator { range, i }
369379

370-
return function next() {
371-
if (i == to) {
372-
return null;
380+
trait::impl(iter::Iterable, Range, struct {
381+
function iter(self) {
382+
return RangeIterator { range = self }
383+
}
384+
});
385+
386+
trait::impl(iter::Iterator, RangeIterator, struct {
387+
function next(self) {
388+
if (self.i == null) {
389+
self.i = self.range.start;
373390
}
374-
let val = i;
375-
i += 1;
376-
return val;
377-
};
378-
}
379391

380-
test::assert(iter::collect(range(10)) == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
392+
if (self.i >= self.range.end) {
393+
return iter::STOP;
394+
}
395+
396+
let { i } = self;
397+
self.i += self.range.step;
398+
return i;
399+
}
400+
});
401+
402+
function range(start, stop=null, step=null) {
403+
if (stop == null) {
404+
return Range { start = 0, stop = start, step = 1 };
405+
} else {
406+
if (step == null) {
407+
step = 1;
408+
}
409+
return Range { start = start, stop = stop, step = step };
410+
}
411+
}
381412
```
382413
383-
Using constructs like these, iteration can be performed like in JavaScript:
384-
```js
414+
It's common to implement `iter::Iterable` for any type that also implements
415+
`iter::Iterator`; that way you can use existing iterators as arguments for
416+
`iter` functions.
417+
418+
Once you have an object that implements the iteration protocol, you can use it
419+
like this:
420+
```c++
385421
iter::foreach(range(10), function (n) {
386422
println(n);
387423
});

0 commit comments

Comments
 (0)