Futures will relay output produced by functions such as cat()
, print()
and str()
. More specifically, output sent to the standard output (stdout) while a future is evaluated will be captured and re-outputted when the value of the future is queried. Importantly, this works identically regardless of future backend used.
For example,
> library(future)
> plan(multiprocess)
> fa <- future({ cat("Hello world!\n"); print(1:3); 42L })
> fb <- future({ str(iris); summary(iris) })
> a <- value(fa)
Hello world!
[1] 1 2 3
> b <- value(fb)
'data.frame': 150 obs. of 5 variables:
$ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
$ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
$ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
$ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
$ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
> a
[1] 42
> b
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100 setosa :50
1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300 versicolor:50
Median :5.800 Median :3.000 Median :4.350 Median :1.300 virginica :50
Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199
3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800
Max. :7.900 Max. :4.400 Max. :6.900 Max. :2.500
Note that the output will be relayed each time value()
is called, e.g.
> a <- value(fa)
Hello world!
[1] 1 2 3
> a <- value(fa)
Hello world!
[1] 1 2 3
Output is relayed the same way when using future assignments (%<-%
). For example,
> library(future)
> plan(multiprocess)
> a %<-% { cat("Hello world!\n"); print(1:3); 42L }
> b %<-% { str(iris); summary(iris) }
> a
Hello world!
[1] 1 2 3
[1] 42
> b
'data.frame': 150 obs. of 5 variables:
$ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
$ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
$ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
$ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
$ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100 setosa :50
1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300 versicolor:50
Median :5.800 Median :3.000 Median :4.350 Median :1.300 virginica :50
Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199
3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800
Max. :7.900 Max. :4.400 Max. :6.900 Max. :2.500
Note how the captured output is relayed followed by the printing of the value. Also, since the future value is only queried once when using future assignments, or more specifically when using promises, the output is only relaid once. For example, querying a
again will only print its value, because it is now a regular R object:
> a
[1] 42
> a
[1] 42
The output is relayed automatically also when using frontends such as future.apply or foreach with doFuture. Again, it works with any future backend. For example,
> library(future.apply)
> plan(future.callr::callr)
> y <- lapply(1:3, FUN = function(x) { cat("x =", x, "\n"); sqrt(x) })
x = 1
x = 2
x = 3
> str(y)
List of 3
$ : num 1
$ : num 1.41
$ : num 1.73
Equivalently,
> library(doFuture)
> registerDoFuture()
> plan(future.callr::callr)
> y <- foreach(x = 1:3) %dopar% { cat("x =", x, "\n"); sqrt(x) })
x = 1
x = 2
x = 3
> str(y)
List of 3
$ : num 1
$ : num 1.41
$ : num 1.73
To capture the output produced by futures, use capture.output()
as you would do when capturing output elsewhere in R. For example,
> library(future)
> fa <- future({ cat("Hello world!\n"); print(1:3); 42L })
> stdout <- capture.output(a <- value(fa))
> stdout
[1] "Hello world!" "[1] 1 2 3"
> a
[1] 42
It is only the standard output that is relayed. It is not possible to relay output send to the standard error (stderr), i.e. output by message()
, cat(..., file = stderr())
, etc. will be lost. This is due to a limitation in R, preventing us from capturing stderr in a reliable way, particularity across all backends.
Copyright Henrik Bengtsson, 2018