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

Reply via email to