---
title: "Community FAQs"
vignette: >
  %\VignetteIndexEntry{Community FAQs}
  %\VignetteEngine{litedown::vignette}
  %\VignetteEncoding{UTF-8}
---


This vignette is designed to provide a knowledgebase for questions posed by the community, and will be added to over time.

### 1. Migration from `future_promise()`

For use within Shiny, it should be straightforward translating ExtendedTask or other async code that was originally written for use with a `promises::future_promise()`.

Note: `future_promise()` exists in the promises package as we had to find a workaround to make `future(...)` always async.
`future(...)` by itself is not always async as it blocks as soon as it runs out of parallel processes on which to run tasks.

`mirai()` on the other hand is built as an async framework, so there's no need for an additional function from the promises package.
You should simply use a `mirai()` directly in place of a `future_promise()`.

**Globals:**

One important difference is that a `future_promise()` by default tries to infer all the global variables that are required by the expression.
If your code depended on this convenience feature then you will need to instead pass these in via the `...` of `mirai()`.
A mirai requires that the expression be self-contained, with any variables or helper functions explicitly supplied to it.

On the other hand, if your code previously used the `globals` argument to supply these variables, then you can often pass that directly to the `.args` of `mirai()`.
Note that this would only work in the case of a named list and not the other forms that `globals` can take.

Regardless of using a `mirai()` or `future_promise()`, we recommend that you pass globals explicitly in production code.
This is as globals detection is never 100% perfect, and there is always some element of guesswork, with edge cases leading to unpredictable results.
Explicit passing of variables allows for transparent and reliable behaviour, remaining robust over time.

**Capture globals using `environment()`:**

`mirai()` allows passing an environment to `...` or to `.args`.
This is especially useful for Shiny ExtendedTask, where it is invoked with a set of arguments.
By using `mirai::mirai({...}, environment())` you automatically capture the variables provided to the invoke method.
See the Shiny vignette for example usage.

**Special Case: `...`:**

A Shiny app may have used `future_promise()` code similar to the following within the server component:

```r
func <- function(x, y){
  Sys.sleep(y)
  runif(x)
}

task <- ExtendedTask$new(
  function(...) future_promise(func(...))
) |> bind_task_button("btn")

observeEvent(input$btn, task$invoke(input$n, input$delay))
```
The equivalent in `mirai()` is achieved by:
```r
task <- ExtendedTask$new(
  function(...) mirai(func(...), func = func, .args = environment())
) |> bind_task_button("btn")
```
Note that here `environment()` captures the `...` that's then used within the mirai expression.

### 2. Setting the random seed

The following example was raised as being potentially counter-intuitive, given that default 'cleanup' settings at each daemon ensures that variables in the global environment, of which `.Random.seed` is one, do not carry over to subsequent runs.


``` r
library(mirai)
daemons(4)

vec <- 1:3
vec2 <- 4:6

# Returns different values: good
mirai_map(list(vec, vec2), \(x) rnorm(x))[]
#> [[1]]
#> [1]  0.001644678 -1.187782046 -0.297140635
#> 
#> [[2]]
#> [1] -0.7211057 -0.8825230 -0.9686437

# Set the seed in the function
mirai_map(list(vec, vec2), \(x) {
  set.seed(123)
  rnorm(x)
})[]
#> [[1]]
#> [1] -0.9685927  0.7061091  1.4890213
#> 
#> [[2]]
#> [1] -0.9685927  0.7061091  1.4890213

# Do not set the seed in the function: still identical results?
mirai_map(list(vec, vec2), \(x) rnorm(x))[]
#> [[1]]
#> [1] -1.8150926  0.3304096 -1.1421557
#> 
#> [[2]]
#> [1] -1.8150926  0.3304096 -1.1421557

daemons(0)
```

The reason the change in random seed persists in all circumstances is due to this being a special case, arising from the use of L'Ecuyer CMRG streams to provide parallel-safe random numbers.

Streams can be thought of as entry points to the psuedo random number line far away from each other to ensure that random results in each daemon are independent from one another.
The random seed is not reset after each mirai call to ensure that however many random draws are made in any mirai call, the next random draw follows on in the stream, and hence have the desired statistical properties.

Hence normally, the random seed should be set once on the host process when daemons are created, rather than in each daemon.

For numerical reproducibility, set the `seed` argument to `daemons()` (see the Random Number Generation section of the reference vignette for further details).

### 3. Accessing package functions during development

A mirai call usually requires package-namespaced functions. However the latest version of a package in development is often loaded dynamically by `devtools::load_all()` or the underlying `pkgload::load_all()` for quick iteration.

In this case, use `everywhere()` to also call `devtools::load_all()` on all (local) daemons. They will then have access to the same functions as your host session for subsequent `mirai()` calls.

### 4. Why does it take time for `mirai()` to execute when it's meant to return immediately?

A `mirai()` call is meant to return almost instantaneously. The same when invoked by a Shiny `ExtendedTask`. The only reason it would take time is if you are passing through large objects, which then need to be serialized to be sent to the parallel process.

Care should be taken when you pass a function or environment to `mirai()` in the `...` or `.args` arguments. This is as a function includes its closure (enclosing environment), and an environment its parent environments. This means you could be passing more than you bargained for.

Generally, `obj_size()` from the lobstr package is a great function for checking the actual size of an object (capturing many cases more accurately than the base R `object.size`).

As mitigation for accidentally passing large objects:

- For functions, use `crate()` from the carrier package. This is what we use in purrr, and it makes sure only what's necessary is 'crated' with the function.
- For environments, consider using `parent.env(e) <- emptyenv()`. This is not required for R6 classes as they are already isolated by default. Regardless of any parent environment, an environment / R6 class could still contain a lot of items that are not needed for the parallel process, in which case consider passing individual members rather than the entire object.

mirai does not rely on options or environment variables at all, so there are no object size limits to set.

This follows mirai's philosophy of being an enabler to widen the possibility frontier for R code, but it does place a responsibility on the user to ensure that the code is sensible and does what is desired.
