Skip to content

Commit 3897c2e

Browse files
Document best-practice use of '...' in anonymous functions [#761]
1 parent b5c7dfa commit 3897c2e

File tree

2 files changed

+98
-1
lines changed

2 files changed

+98
-1
lines changed

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Package: future
2-
Version: 1.34.0-9114
2+
Version: 1.34.0-9115
33
Title: Unified Parallel and Distributed Processing in R for Everyone
44
Imports:
55
digest,

vignettes/future-4-issues.md.rsp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,103 @@ or equivalently
249249
Note, do _not_ use `library()` or `loadNamespace()` to resolve these problems. It is always better to use the above `packages` approach.
250250

251251

252+
## '...' used in an incorrect context
253+
254+
In R, we can use the `...` construct is used to refer to zero or more
255+
arguments. For example, we can use as in:
256+
257+
```r
258+
my_mean <- function(x, ...) mean(x, ...)
259+
260+
y <- my_mean(1:10, trim = 0.1, na.rm = FALSE)
261+
```
262+
263+
This makes sure that the `trim` and the `na.rm` arguments are pass
264+
down to the `mean()` function.
265+
266+
We can also use it to pass arguments in map-reduce calls to anonymous
267+
functions as in:
268+
269+
```r
270+
X <- rnorm(10)
271+
y <- lapply(X, FUN = function(x, ...) {
272+
round(x, ...)
273+
}, digits = 3)
274+
```
275+
276+
Note how `digits = 3` is pass to the anonymous function via its `...`
277+
argument, which is then passed on to `round()`, effectively calling
278+
`round(X[[1]], digits = 3)`, `round(X[[2]], digits = 3)`, and so on.
279+
280+
If we take this one step further, we might see things like:
281+
282+
```r
283+
my_fcn <- function(X, ...) { ## outer '...'
284+
y <- lapply(X, FUN = function(x, ...) { ## inner '...'
285+
round(x, ...) ## inner '...'
286+
}, ...) ## outer '...'
287+
y
288+
}
289+
290+
X <- rnorm(10)
291+
y <- my_fcn(X, digits = 3)
292+
```
293+
294+
In this case, we have two levels of `...` arguments; one for
295+
`my_fcn()` and one for the anonymous function. Note how the `...`
296+
arguments for `my_fcn()` are passed down to the anonymous function by
297+
specifying `...` as an final argument to `lapply()`.
298+
299+
The above is the ideal and proper way to pass down `...`. However, it
300+
is not uncommon to see that the `...` is used as a global variable in
301+
anonymous functions. For example, you might find:
302+
303+
```r
304+
my_fcn <- function(X, ...) { ## outer '...'
305+
y <- lapply(X, FUN = function(x) {
306+
round(x, ...) ## outer '...' as global variables
307+
})
308+
y
309+
}
310+
```
311+
312+
This will also work, because `...` becomes a global variable in the
313+
environment of the anonymous function. Although we know that relying
314+
on global variables is a bad idea, this one often slips through.
315+
316+
If we attempt to do the same with the future framework, or other
317+
parallel frameworks, it might not work. For example, using:
318+
319+
```r
320+
my_fcn <- function(X, ...) {
321+
y <- future_lapply(X, FUN = function(x) {
322+
round(x, ...)
323+
})
324+
y
325+
}
326+
```
327+
328+
might result in an error on:
329+
330+
```
331+
Error: '...' used in an incorrect context
332+
```
333+
334+
Even you do not get this error, it is always a good idea to make sure
335+
`...` is passed as an argument all the way down to where it is used,
336+
e.g.
337+
338+
```r
339+
my_fcn <- function(X, ...) {
340+
y <- future.apply::future_lapply(X, FUN = function(x, ...) {
341+
round(x, ...)
342+
}, ...)
343+
y
344+
}
345+
```
346+
347+
348+
252349
## Non-exportable objects
253350

254351
Certain types of objects are tied to a given R session and cannot be passed along to another R process (a "worker"). An example of a non-exportable object is is XML objects of the **[xml2]** package. If we attempt to use those in parallel processing, we may get a error when the future is evaluated (or just invalid results depending on how they are used), e.g.

0 commit comments

Comments
 (0)