Skip to content

Modifying an R6 class from within mirai or mirai_map #249

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
avhz opened this issue Apr 9, 2025 · 1 comment
Open

Modifying an R6 class from within mirai or mirai_map #249

avhz opened this issue Apr 9, 2025 · 1 comment

Comments

@avhz
Copy link

avhz commented Apr 9, 2025

Hi @shikokuchuo, as requested, here's an attempt at a reprex regarding my issue mentioned over at nanonext.

It is very simplified, but roughly shows my problem:

Foo <- R6::R6Class(
    "Foo",
    public = list(

        ## FIELDS
        bar = NULL,

        ## METHODS
        iterate_sync = function() {
            purrr::map(1:5, \(i) self$bar[i] <- i)
            return(self$bar)
        },
        iterate_async = function() {
            with(
                mirai::daemons(4),
                mirai::mirai_map(1:5, \(i) self$bar[i] <- i)
            )
            return(self$bar)
        }
    )
)

Running the iterate_sync method gives:

> foo <- Foo$new()
> foo$bar
NULL
> foo$iterate_sync()
[1] 1 2 3 4 5
> foo$bar
[1] 1 2 3 4 5

Whereas iterate_async:

> foo <- Foo$new()
> foo$bar
NULL
> foo$iterate_async()
NULL
> foo$bar
NULL

Obviously this example is a bit silly since I can just run the map and assign to self$bar afterwards.

But my real case is more complicated and multiple fields are modified and so on, and if there is a simple fix to this that would be nice, to avoid a re-write.

I am using Mirai elsewhere with success, by passing data via .args and/or ..., but have not been able to get this case working.

@shikokuchuo
Copy link
Member

You should think of the mirai call needing to be self-sufficient. The components are sent to a different R process. You won't have access to anything outside of the call unless you specifically pass it in.

So a possible solution would be:

mirai::daemons(4)

Foo <- R6::R6Class(
  "Foo",
  public = list(
    
    ## FIELDS
    bar = NULL,
    
    ## METHODS
    iterate_sync = function() {
      purrr::map(1:5, \(i) self$bar[i] <- i)
      return(self$bar)
    },
    iterate_async = function() {
      self$bar <- mirai::mirai_map(1:5, \(i) bar[i] <- i, bar = self$bar)
      return(self$bar)
    }
  )
)

foo$iterate_async()
#> < mirai map [0/5] >
foo$bar
#> < mirai map [5/5] >
foo$bar[.flat]
#> [1] 1 2 3 4 5
  1. It's good practice to keep the daemons() call separate, as you (or another user) may want to vary that each time, it shouldn't be baked into the function.
  2. If you want async, you return a mirai_map object as above. You can collect it later e.g. using a flatmap which gives the same as the sync variant. If you just want parallel, you'd collect it inside the function before assigning to self$bar.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants