1<%@meta language="R-vignette" content="-------------------------------- 2%\VignetteIndexEntry{A Future for R: Text and Message Output} 3%\VignetteAuthor{Henrik Bengtsson} 4%\VignetteKeyword{R} 5%\VignetteKeyword{package} 6%\VignetteKeyword{vignette} 7%\VignetteKeyword{future} 8%\VignetteKeyword{promise} 9%\VignetteKeyword{output} 10%\VignetteKeyword{standard output} 11%\VignetteKeyword{stdout} 12%\VignetteKeyword{standard error} 13%\VignetteKeyword{stderr} 14%\VignetteKeyword{message} 15%\VignetteKeyword{condition} 16%\VignetteEngine{R.rsp::rsp} 17%\VignetteTangle{FALSE} 18--------------------------------------------------------------------"%> 19# <%@meta name="title"%> 20 21Futures 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 ("relayed") when the value of the future is queried_. Messages produced by `message()`, which formally are R conditions are also captured and resignaled ("relayed") as messages in the main R session. Importantly, this works identically regardless of future backend used. 22 23For simplicitly, lets start with an illustration on how standard output ("stdout") is captured and relayed: 24 25```r 26> library("future") 27> plan(multisession) 28 29> fa <- future({ cat("Hello world!\n"); print(1:3); 42L }) 30> fb <- future({ str(iris); summary(iris) }) 31 32> a <- value(fa) 33Hello world! 34[1] 1 2 3 35> b <- value(fb) 36'data.frame': 150 obs. of 5 variables: 37 $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ... 38 $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ... 39 $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ... 40 $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ... 41 $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ... 42 43> a 44[1] 42 45> b 46 Sepal.Length Sepal.Width Petal.Length Petal.Width Species 47 Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100 setosa :50 48 1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300 versicolor:50 49 Median :5.800 Median :3.000 Median :4.350 Median :1.300 virginica :50 50 Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199 51 3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800 52 Max. :7.900 Max. :4.400 Max. :6.900 Max. :2.500 53``` 54 55Note that the captured standard output (stdout) will be relayed _each_ time `value()` is called, e.g. 56```r 57> a <- value(fa) 58Hello world! 59[1] 1 2 3 60 61> a <- value(fa) 62Hello world! 63[1] 1 2 3 64``` 65 66Output is relayed the same way when using future assignments (`%<-%`). For example, 67 68```r 69> library("future") 70> plan(multisession) 71 72> a %<-% { cat("Hello world!\n"); print(1:3); 42L } 73> b %<-% { str(iris); summary(iris) } 74 75> a 76Hello world! 77[1] 1 2 3 78[1] 42 79> b 80'data.frame': 150 obs. of 5 variables: 81 $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ... 82 $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ... 83 $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ... 84 $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ... 85 $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ... 86 Sepal.Length Sepal.Width Petal.Length Petal.Width Species 87 Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100 setosa :50 88 1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300 versicolor:50 89 Median :5.800 Median :3.000 Median :4.350 Median :1.300 virginica :50 90 Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199 91 3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800 92 Max. :7.900 Max. :4.400 Max. :6.900 Max. :2.500 93``` 94 95Note 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 relayed once. For example, querying `a` again will only print its value, because it is now a regular R object: 96```r 97> a 98[1] 42 99> a 100[1] 42 101``` 102 103Next, lets see what happens if we use `message()` to produce output: 104 105```r 106> library("future") 107> plan(multisession) 108> fa <- future({ message("Hello world!"); 42L }) 109> value(fa) 110Hello world! 111[1] 42 112``` 113 114Note that contrary to the captured stdout, which is captured as one single block output, messages are conditions that are captured separately. Unfortunately, it is _not_ possible to preserve the ordering of interweaved stdout and message output. When using futures, stdout output will always be relayed first followed by each of the individual conditions captured. For example, 115 116 117```r 118> library("future") 119> plan(multisession) 120> fa <- future({ message("Hello"); print(1:3); message("world!"); cat("ping\n"); 42L }) 121> value(fa) 122[1] 1 2 3 ## <= stdout as a single ... 123ping ## <= ... block of output 124Hello ## <= 1st message 125world! ## <= 2nd message 126[1] 42 127``` 128 129 130## Future frontends 131 132The 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, 133 134```r 135> library("future.apply") 136> plan(future.callr::callr) 137 138> y <- future_lapply(1:3, FUN = function(x) { cat("x =", x, "\n"); message("x : ", x); sqrt(x) }) 139x = 1 140x = 2 141x = 3 142x : 1 ## <= 1st message 143x : 2 ## <= 2nd message 144x : 3 ## <= 3rd message 145 146> str(y) 147List of 3 148 $ : num 1 149 $ : num 1.41 150 $ : num 1.73 151``` 152 153Equivalently, 154 155```r 156> library("doFuture") 157> registerDoFuture() 158> plan(future.callr::callr) 159 160> y <- foreach(x = 1:3) %dopar% { cat("x =", x, "\n"); message("x : ", x); sqrt(x) } 161x = 1 162x = 2 163x = 3 164x : 1 ## <= 1st message 165x : 2 ## <= 2nd message 166x : 3 ## <= 3rd message 167 168> str(y) 169List of 3 170 $ : num 1 171 $ : num 1.41 172 $ : num 1.73 173``` 174 175 176## Capturing output 177 178To capture the output produced by futures, use `capture.output()` as you would do when capturing output elsewhere in R. For example, 179 180```r 181> library("future") 182> fa <- future({ cat("Hello world!\n"); print(1:3); 42L }) 183> stdout <- capture.output(a <- value(fa)) 184> stdout 185[1] "Hello world!" "[1] 1 2 3" 186> a 187[1] 42 188``` 189 190 191## Suppressing messages 192 193```r 194> library("future") 195> plan(multisession) 196> fa <- future({ message("Hello"); print(1:3); message("world!"); cat("ping\n"); 42L }) 197> suppressMessages(a <- value(fa)) 198[1] 1 2 3 199ping 200> a 201[1] 42 202``` 203 204 205## Known limitations 206 207It is only the standard output that is relayed. It is _not possible_ to relay output send to the standard error (stderr), e.g. output by `cat(..., file = stderr())` will be lost. This is due to a [limitation in R](https://github.com/HenrikBengtsson/Wishlist-for-R/issues/55), preventing us from capturing stderr in a reliable way, particularity across all backends. However, note that the captured messages by `message()` are outputted to stderr (as expected) when resignaled/relayed. 208 209 210[foreach]: https://cran.r-project.org/package=foreach 211[future]: https://cran.r-project.org/package=future 212[future.apply]: https://cran.r-project.org/package=future.apply 213[doFuture]: https://cran.r-project.org/package=doFuture 214[globals]: https://cran.r-project.org/package=globals 215[listenv]: https://cran.r-project.org/package=listenv 216