Clever! If going for non-local returns, probably best for ergonomics to pass in a closure (see e.g. `callCC()`). If only to avoid accidental jumps while debugging.
But... do we need more lazy evaluation tricks in the language or fewer? It's probably more idiomatic to express non-local returns with condition signals like `stopIteration()`. There's something to be said for explicit and simple control flow though, via handling of returned values. > Note that it is trivial to create a unique sentinel value -- any newly > created closure (i.e. function() NULL) will do, as it will only > compare identical() with itself. Until you try that in the global env right? Then the risk of collision slightly increases. Unless you make your closure more unique via `body()`, but then might as well use a conventional sentinel. Best, Lionel On Tue, Aug 12, 2025 at 1:45 AM Peter Meilstrup <peter.meilst...@gmail.com> wrote: > > Passing the sentinel value as an argument to the iteration method is > the approach taken in my package `iterors` on CRAN. If the sentinel > value argument is evaluated lazily, this lets you pass calls to things > like 'stop', 'break' or 'return,' which will be called to signal end > of iteration. This makes for some nice compact and performant > iteration idioms: > > iter <- as.iteror(obj) > total <- 0 > repeat {total <- total + nextOr(iter, break)} > > Note that iteror is just a closure with one optional argument and a > class attribute, so you can skip using s3 nextOr method and call it > directly: > > nextElem <- as.iteror(obj) > repeat {total <- total + nextElem(break)} > > For backward compatibility with the iterators package, the default > sentinel value for iterors is `stop("StopIteration")`. > > Note that it is trivial to create a unique sentinel value -- any newly > created closure (i.e. function() NULL) will do, as it will only > compare identical() with itself. > > sigil <- \() NULL > next <- as.iteror(obj) > while (!identical(item <-next(sigil), sigil)) { > doStuff(item) > } > > Peter Meilstrup > > On Mon, Aug 11, 2025 at 5:56 PM Lionel Henry via R-devel > <r-devel@r-project.org> wrote: > > > > 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 ______________________________________________ R-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel