-
Notifications
You must be signed in to change notification settings - Fork 17
Open
Description
Wrote this up for future_promise() usage in #62 . It did not make sense for the vignette. So I added into an issue for the time being.
## Variable scope in `future_promise()` and `future::future()`
`future_promise()` is a promise **first** and executes using `future::future()` **second**. When using `future_promise()`, you should use the same precautions that you would use with a regular `promises::promise()`.
When `future` blocks on the main R session, this prevents values from being changed before they are submitted to the external worker. While a `promise` waits to be executed, it is known that variables that have **not been `forced()`** or properly **scoped** can change from their expected values before evaluation occurs. This can also occur with properly scoped environment values as only the _pointer_ to the environment is static. This allows for the values within the environment to be altered before the `promise` is executed, which is likely undesirable.
#### Scope
In the example below, the variable `i` is not forced to a specific value for the promise. With the promise resolving within the global environment, the latest `i` value will be used for all of the promises waiting to resolve.
```r
items <- list()
for (i in 1:10) {
items <- c(items, list(
promise_resolve(TRUE) %...>% {i}
))
}
promise_all(.list = items) %...>%
{ print(unlist(.)) }
# #> [1] 10 10 10 10 10 10 10 10 10 10
```
To combat variable scoping issues, functions can be used to create local environments that will _scope_ the expected `i` value.
```r
lapply(1:10, function(i) {
promise_resolve(TRUE) %...>% {i}
}) %>%
promise_all(.list = .) %...>%
{ print(unlist(.)) }
#> [1] 1 2 3 4 5 6 7 8 9 10
```
#### Changing environments
Environments can have their values changed after the original promise creation. This can cause unexpected behavior when evaluating a promise.
For example, the environment `env` below will have its value `a` changed from `1` to `2` before the promise is resolved. This causes the unexpected value of `2` to be returned in the promise.
```r
{
env <- new.env()
env$a <- 1
promise_resolve(TRUE) %...>%
{ Sys.sleep(1); env$a } %...>%
{ print(.) }
env$a <- 2
print("Changed env$a")
}
#> [1] "Changed env$a"
#> [1] 2
```
To address lazy evaluation and environment variable issues, we can store the values to a local variable and force their evaluation.
Fixing the example, we can capture the value `a` (or turn the environment into a `list()`).
```r
{
env <- new.env()
env$a <- 1
a_value <- force(env$a)
promise_resolve(TRUE) %...>%
{ Sys.sleep(1); a_value } %...>%
{ print(.) }
env$a <- 2
print("done")
}
#> [1] "done"
#> [1] 1
```
The expression in `future_promise(expr)` will behave like a promise and should have its volitile variables scoped and `force()`ed to achieve similar evaluations when using `future::future()` directly.
Metadata
Metadata
Assignees
Labels
No labels