Hello, A couple of comments:
- Regarding the closure + sentinel approach, also implemented in coro (https://github.com/r-lib/coro/blob/main/R/iterator.R), it's more robust for the sentinel to always be a temporary value. If you store the sentinel in a list or a namespace, it might inadvertently close iterators when iterating over that collection. That's why the coro sentinel is created with `coro::exhausted()` rather than exported from the namespace as a constant object. The sentinel can be equivalently created with `as.symbol(".__exhausted__.")`, the main thing to ensure robustness is to avoid storing it and always create it from scratch. The approach of passing the sentinel by argument (which I see in the example in your mail but not in the linked documentation of approach 3) also works if the iterator loop passes a unique sentinel. Having a default of `NULL` makes it likely to get unexpected exhaustion of iterators when a sentinel is not passed in though. - It's very useful to _close_ iterators for resource cleanup. It's the responsibility of an iterator loop (e.g. `for` but could be other custom tools invoking the iterator) to close them. See https://github.com/r-lib/coro/pull/58 for an interesting application of iterator closing, allowing robust support of `on.exit()` expressions in coro generators. To implement iterator closing with the closure approach, an iterator may optionally take a `close` argument. A `true` value is passed on exit, instructing the iterator to clean up resources. Best, Lionel On Mon, Aug 11, 2025 at 3:24 PM Tomasz Kalinowski <kalinows...@gmail.com> wrote: > > Hi all, > > A while back, Hadley and I explored what an iteration protocol for R > might look like. We worked through motivations, design choices, and edge > cases, which we documented here: > https://github.com/t-kalinowski/r-iterator-ideas > > At the end of this process, I put together a patch to R (with tests) and > would like to invite feedback from R Core and the broader community: > https://github.com/r-devel/r-svn/pull/130/files?diff=unified&w=1 > > In summary, the overall design is a minimal patch. It introduces no > breaking changes and essentially no new overhead. There are two parts. > > 1. Add a new `as.iterable()` S3 generic, with a default identity > method. This provides a user-extensible mechanism for selectively > changing the iteration behavior for some object types passed to > `for`. `as.iterable()` methods are expected to return anything that > `for` can handle directly, namely, vectors or pairlists, or (new) a > closure. > > 2. `for` gains the ability to accept a closure for the iterable > argument. A closure is called repeatedly for each loop iteration > until the closure returns an `exhausted` sentinel value, which it > received as an input argument. > > Here is a small example of using the iteration protocol to implement a > sequence of random samples: > > ``` r > SampleSequence <- function(n) { > i <- 0 > function(done = NULL) { > if (i >= n) { > return(done) > } > i <<- i + 1 > runif(1) > } > } > > for(sample in SampleSequence(2)) { > print(sample) > } > > # [1] 0.7677586 > # [1] 0.355592 > ``` > > Best, > Tomasz > > ______________________________________________ > R-devel@r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel ______________________________________________ R-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel