Concurrent programming is made straightforward by async-await functions.
Whenever you are waiting for a result that may take a while (downloading
a file, computing a value in an external process), use await(). The
argument to await() must return a promise from the
promises package.
Concurrent code based on promises can quickly become hard to write and
follow. In the following artificial example, we wait for a download to
complete, then decide to launch a computation in an external process
depending on a property of the downloaded data. We also handle some
errors specifically.
yield() and await() can be used in loops, if/else branches,
tryCatch() expressions, or any combinations of these. However they
can’t be used as function arguments. These will cause errors:
Fortunately it is easy to rewrite the code to work around this
limitation:
generator(function() {
x <- yield("foo")
list(x)
})
async(function() {
x <- await(foo())
list(x)
})
How does it work
Coroutines are an abstraction for state
machines
in languages that support them. Conversely, you can implement coroutines
by rewriting the code source provided by the user as a state machine.
Pass internals = TRUE to the print methods of coroutines to reveal the
state machine that is running under the hood:
Despite this transformation of source code, browser() and
step-debugging still work as you would expect. This is because coro
keeps track of the source references from the original code.
Acknowledgements
The regenerator Javascript
package which uses a similar transformation to implement generators
and async functions in older versions of Javascript.
Gabor Csardi for many interesting discussions about concurrency and
the design of coro.
coro
Overview
coro implements coroutines for R, i.e. functions that can be suspended and resumed later on. There are two kinds:
Supported features:
tryCatch()on.exit()expressions and stack-based cleanup such as provided bylocal_functions in the withr packagebrowser()within coroutinesCompatibility with:
Attach the package to follow the examples:
Async/await functions
Concurrent programming is made straightforward by async-await functions. Whenever you are waiting for a result that may take a while (downloading a file, computing a value in an external process), use
await(). The argument toawait()must return a promise from the promises package.Concurrent code based on promises can quickly become hard to write and follow. In the following artificial example, we wait for a download to complete, then decide to launch a computation in an external process depending on a property of the downloaded data. We also handle some errors specifically.
Rewriting this function with async/await greatly simplifies the code:
Generators
Generators are based on a simple iteration protocol:
exhausted.The
generator()function creates a generator factory which returns generator instances:A generator instance is an iterator function which yields values:
Collect all remaining values from an iterator with
collect():Iterate over an iterator with
loop():See
vignette("generator")for more information.Compatibility with the reticulate package
Python iterators imported with the reticulate package are compatible with
loop()andcollect():They can also be composed with coro generators:
Limitations
yield()andawait()can be used in loops, if/else branches,tryCatch()expressions, or any combinations of these. However they can’t be used as function arguments. These will cause errors:Fortunately it is easy to rewrite the code to work around this limitation:
How does it work
Coroutines are an abstraction for state machines in languages that support them. Conversely, you can implement coroutines by rewriting the code source provided by the user as a state machine. Pass
internals = TRUEto the print methods of coroutines to reveal the state machine that is running under the hood:Despite this transformation of source code,
browser()and step-debugging still work as you would expect. This is because coro keeps track of the source references from the original code.Acknowledgements
The regenerator Javascript package which uses a similar transformation to implement generators and async functions in older versions of Javascript.
Gabor Csardi for many interesting discussions about concurrency and the design of coro.
Installation
Install the development version from github with:
Code of Conduct
Please note that the coro project is released with a Contributor Code of Conduct. By contributing to this project, you agree to abide by its terms.