Skip to content
Prev 63149 / 63424 Next

An iteration protocol

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 <kalinowskit at gmail.com> wrote: