Movatterモバイル変換


[0]ホーム

URL:


Package 'coro'

Title:'Coroutines' for R
Description:Provides 'coroutines' for R, a family of functions that can be suspended and resumed later on. This includes 'async' functions (which await) and generators (which yield). 'Async' functions are based on the concurrency framework of the 'promises' package. Generators are based on a dependency free iteration protocol defined in 'coro' and are compatible with iterators from the 'reticulate' package.
Authors:Lionel Henry [aut, cre], Posit Software, PBC [cph, fnd]
Maintainer:Lionel Henry <[email protected]>
License:MIT + file LICENSE
Version:1.1.0.9000
Built:2025-11-19 07:04:15 UTC
Source:https://github.com/r-lib/coro

Help Index


Transform an object to an iterator

Description

as_iterator() is a generic function that transforms its input toaniterator function. The default implementationis as follows:

  • Functions are returned as is.

  • Other objects are assumed to be vectors withlength() and[[methods.

Methods must return functions that implement coro'siterator protocol.

as_iterator() is called by coro on the RHS of⁠in⁠ inforloops. This applies withingenerators,async functions, andloop().

Usage

as_iterator(x)## Default S3 method:as_iterator(x)

Arguments

x

An object.

Value

An iterable function.

Examples

as_iterator(1:3)i<- as_iterator(1:3)loop(for(xin i) print(x))

Make an async function

Description

async() functions are building blocks for cooperativeconcurrency.

  • They areconcurrent because they are jointly managed by ascheduler in charge of running them.

  • They arecooperative because they decide on their own when theycan no longer make quick progress and need toawait someresult. This is done with theawait() keyword which suspendsthe async function and gives control back to the scheduler. Thescheduler waits until the next async operation is ready to makeprogress.

The async framework used byasync() functions is implemented inthelater andpromises packages:

  • You can chain async functions created with coro to promises.

  • You can await promises. You can also await futures created withthefuture packagebecause they are coercible to promises.

Usage

async(fn)await(x)

Arguments

fn

An anonymous function within whichawait() calls areallowed.

x

An awaitable value, i.e. apromise.

Value

A function that returns apromises::promise() invisibly.

See Also

async_generator() andawait_each();coro_debug() for step-debugging.

Examples

# This async function counts down from `n`, sleeping for 2 seconds# at each iteration:async_count_down<- async(function(n){while(n>0){    cat("Down", n,"\n")    await(async_sleep(2))    n<- n-1}})# This async function counts up until `stop`, sleeping for 0.5# seconds at each iteration:async_count_up<- async(function(stop){  n<-1while(n<= stop){    cat("Up", n,"\n")    await(async_sleep(0.5))    n<- n+1}})# You can run these functions concurrently using `promise_all()`if(interactive()){  promises::promise_all(async_count_down(5), async_count_up(5))}

Collect elements of an asynchronous iterator

Description

async_collect() takes an asynchronous iterator, i.e. an iterablefunction that is also awaitable.async_collect() returns anawaitable that eventually resolves to a list containing the valuesreturned by the iterator. The values are collected until exhaustionunlessn is supplied. The collection is grown geometrically forperformance.

Usage

async_collect(x, n=NULL)

Arguments

x

An iterator function.

n

The number of elements to collect. Ifx is an infinitesequence,n must be supplied to prevent an infinite loop.

Examples

# Emulate an async stream by yielding promises that resolve to the# elements of the input vectorgenerate_stream<- async_generator(function(x)for(eltin x) yield(elt))# You can await `async_collect()` in an async function. Once the# list of values is resolved, the async function resumes.async(function(){  stream<- generate_stream(1:3)  values<- await(async_collect(stream))  values})

Construct an async generator

Description

An async generator constructs iterable functions that are alsoawaitables. They support both theyield() andawait() syntax.An async iterator can be looped within async functions anditerators usingawait_each() on the input of afor loop.

The iteration protocol is derived from the one described initerator. An async iterator always returns apromise. When the iterator is exhausted, it returns a resolvedpromise to the exhaustion sentinel.

Usage

async_generator(fn)await_each(x)

Arguments

fn

An anonymous function describing an async generatorwithin whichawait() calls are allowed.

x

An awaitable value, i.e. apromise.

Value

A generator factory. Generators constructed with thisfactory always returnpromises::promise().

See Also

async() for creating awaitable functions;async_collect() for collecting the values of an async iterator;coro_debug() for step-debugging.

Examples

# Creates awaitable functions that transform their inputs into a streamgenerate_stream<- async_generator(function(x)for(eltin x) yield(elt))# Maps a function to a streamasync_map<- async_generator(function(.i, .fn,...){for(eltin await_each(.i)){    yield(.fn(elt,...))}})# Example usage:if(interactive()){  library(magrittr)  generate_stream(1:3)%>% async_map(`*`,2)%>% async_collect()}

Sleep asynchronously

Description

Sleep asynchronously

Usage

async_sleep(seconds)

Arguments

seconds

The number of second to sleep.

Value

A chainable promise.


Iterate over iterator functions

Description

loop() andcollect() are helpers for iterating overiterator functions such asgenerators.

  • loop() takes afor loop expression in which the collectioncan be an iterator function.

  • collect() loops over the iterator and collects the values in alist.

Usage

collect(x, n=NULL)loop(loop)

Arguments

x

An iterator function.

n

The number of elements to collect. Ifx is an infinitesequence,n must be supplied to prevent an infinite loop.

loop

Afor loop expression.

Value

collect() returns a list of values;loop() returnstheexhausted() sentinel, invisibly.

See Also

async_collect() for async generators.

Examples

generate_abc<- generator(function()for(xin letters[1:3]) yield(x))abc<- generate_abc()# Collect 1 element:collect(abc, n=1)# Collect all remaining elements:collect(abc)# With exhausted iterators collect() returns an empty list:collect(abc)# With loop() you can use `for` loops with iterators:abc<- generate_abc()loop(for(xin abc) print(x))

Debug a generator or async function

Description

  • Callcoro_debug() on agenerator(),async(), orasync_generator() function to enable step-debugging.

  • Alternatively, setoptions(coro_debug = TRUE) forstep-debugging through all functions created with coro.

Usage

coro_debug(fn, value=TRUE)

Arguments

fn

A generator factory or an async function.

value

Whether to debug the function.


Create a generator function

Description

generator() creates an generator factory. A generator is aniterator function that can pause its execution withyield() and resume from where it left off. Because they managestate for you, generators are the easiest way to createiterators. Seevignette("generator").

The following rules apply:

  • Yielded values do not terminate the generator. If you call thegenerator again, the execution resumes right after the yieldingpoint. All local variables are preserved.

  • Returned values terminate the generator. If called again after areturn(), the generator keeps returning theexhausted()sentinel.

Generators are compatible with all features based on the iteratorprotocol such asloop() andcollect().

Usage

generator(fn)gen(expr)

Arguments

fn

A function template for generators. The function canyield() values. Within a generator,for loops haveiterator support.

expr

A yielding expression.

See Also

yield(),coro_debug() for step-debugging.

Examples

# A generator statement creates a generator factory. The# following generator yields three times and then returns `"d"`.# Only the yielded values are visible to the callers.generate_abc<- generator(function(){  yield("a")  yield("b")  yield("c")"d"})# Equivalently:generate_abc<- generator(function(){for(xin c("a","b","c")){    yield(x)}})# The factory creates generator instances. They are iterators# that you can call successively to obtain new values:abc<- generate_abc()abc()abc()# Once a generator has returned it keeps returning `exhausted()`.# This signals to its caller that new values can no longer be# produced. The generator is exhausted:abc()abc()# You can only exhaust a generator once but you can always create# new ones from a factory:abc<- generate_abc()abc()# As generators implement the coro iteration protocol, you can use# coro tools like `loop()`. It makes it possible to loop over# iterators with `for` expressions:loop(for(xin abc) print(x))# To gather values of an iterator in a list, use `collect()`. Pass# the `n` argument to collect that number of elements from a# generator:abc<- generate_abc()collect(abc,1)# Or drain all remaining elements:collect(abc)# coro provides a short syntax `gen()` for creating one-off# generator _instances_. It is handy to adapt existing iterators:numbers<-1:10odds<- gen(for(xin numbers)if(x%%2!=0) yield(x))squares<- gen(for(xin odds) yield(x^2))greetings<- gen(for(xin squares) yield(paste("Hey", x)))collect(greetings)# Arguments passed to generator instances are returned from the# `yield()` statement on reentry:new_tally<- generator(function(){  count<-0while(TRUE){    i<- yield(count)    count<- count+ i}})tally<- new_tally()tally(1)tally(2)tally(10)

Iterator protocol

Description

Aniterator is a function that implements the followingprotocol:

  • Calling the function advances the iterator. The new value isreturned.

  • When the iterator is exhausted and there are no more elements to return,coro::exhausted() or (equivalently)as.symbol(".__exhausted__.") isreturned. This signals exhaustion to the caller.

  • Once an iterator has signalled exhaustion, all subsequentinvokations must consistently returncoro::exhausted() oras.symbol(".__exhausted__.").

  • The iterator function may have aclose argument taking booleanvalues. When passed aTRUE value, it indicates early terminationand the iterator is given the opportunity to clean up resources.

    Cleanup must only be performed once, even if the iterator is calledmultiple times withclose = TRUE.

    An iterator is allowed to not have anyclose argument. Iteratordrivers must check for the presence of the argument. If not present,the iterator can be dropped without cleanup.

    An iterator passedclose = TRUE must returncoro::exhausted() andonce closed, an iterator must returncoro::exhausted() when calledagain.

iterator <- as_iterator(1:3)# Calling the iterator advances ititerator()#> [1] 1iterator()#> [1] 2# This is the last valueiterator()#> [1] 3# Subsequent invokations return the exhaustion sentineliterator()#> .__exhausted__.

Because iteration is defined by a protocol, creating iterators isfree of dependency. However, it is often simpler to createiterators withgenerators, seevignette("generator"). To loop over an iterator, it is simplerto use theloop() andcollect() helpers provided in thispackage.

Usage

exhausted()is_exhausted(x)

Arguments

x

An object.

Properties

Iterators arestateful. Advancing the iterator creates apersistent effect in the R session. Also iterators areone-way. Once you have advanced an iterator, there is no goingback and once it is exhausted, it stays exhausted.

Iterators are not necessarily finite. They can also representinfinite sequences, in which case trying to exhaust them is aprogramming error that causes an infinite loop.

The exhausted sentinel

Termination of iteration is signalled via a sentinel value,as.symbol(".__exhausted__."). Alternative designs include:

  • A condition as in python.

  • A rich value containing a termination flag as in Javascript.

The sentinel design is a simple and efficient solution but it has adownside. If you are iterating over a collection of elements thatinadvertently contains the sentinel value, the iteration will beterminated early. To avoid such mix-ups, the sentinel should onlybe used as a temporary value. It should be created from scratch bya function likecoro::exhausted() and never stored in a containeror namespace.


Yield a value from a generator

Description

Theyield() statement suspendsgenerator() functions. It workslikereturn() except that the function continues execution at theyielding point when it is called again.

yield() can be called within loops and if-else branches but fortechnical reasons it can't be used anywhere in R code:

  • yield() cannot be called as part of a function argument. Codesuch aslist(yield()) is illegal.

  • yield() does not cross function boundaries. You can't use it alambda function passed tolapply() for instance.

Usage

yield(x)

Arguments

x

A value to yield.

See Also

generator() for examples.



[8]ページ先頭

©2009-2025 Movatter.jp