Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using external classes with Shiny, R and futures

Tags:

r

future

shiny

I am trying to put together a framework to get Shiny working asynchronously on a set of classes I have, using futures and possibly promises. I have put together a test project using a set of external modules which mimic my real setup.

Note: I have also tried to implement the exact same call that is throwing the error in this framework: FutureProcessor.R, and the returned error is identical.

Basically, the button click calls a function that instantiates an instance of a class, which then carries out a simple calculation. When run with the first button as a straight process, this works fine. However when I run it using the %<-% assigmment it returns the following error: Warning: Error in getClass: "cTest" is not a defined class

It's clear to me that I am not getting this right! However I am not sure whether what I am trying to do is even possible?

Setup as follows:

Shiny app:

## Load required libraries
pacman::p_load(shiny, here, promises, future)

setwd(here())
source(here("testing.R"))
source(here("TestClass.R"))
plan(multisession)

# Define UI
ui <- fluidPage(

   # Application title
   titlePanel("Test external classes"),

   # Sidebar 
   sidebarLayout(
      sidebarPanel(
         actionButton("clickMe", "I work"),
         actionButton("clickMeToo", "I don't work")
      ),

      # Show a text output
      mainPanel(
         verbatimTextOutput("outputText1"),
         verbatimTextOutput("outputText2")
      )
   )
)

# Define server logic 
server <- function(input, output) {

  myResult <- NULL

   observeEvent(input$clickMe, {

     ## This works:
      myResult <<- testFutures()
      output$outputText1 <- renderText({paste0("test: ", myResult$Item3)})

   })
   observeEvent(input$clickMeToo, {
     ## This works not:
     myResult %<-% {testFutures()}
     output$outputText2 <- renderText({paste0("test: ", myResult$Item3)})
   })
}

# Run the application 
shinyApp(ui = ui, server = server)

My test class:

cTest <- setRefClass("cTest", 
                                     fields=list(

                                       Item1="numeric",
                                       Item2="numeric",
                                       Item3= "numeric"),

                                     methods = list(
                                       Reset = function() {
                                         Item1 <<- 0
                                         Item2 <<- 0
                                         Item3 <<- 0
                                       },
                                       AddUp = function() {
                                         Item3 <<- Item1 + Item2
                                       }
                                     )

My test function:

testFutures <- function() {
  output <- new ("cTest")
  output$Reset()
  output$Item1 <- 3
  output$Item2 <- 4
  output$AddUp()
  return(output)
}
like image 884
Hester Lyons Avatar asked Oct 19 '18 08:10

Hester Lyons


1 Answers

I think there are a few issues with using reference classes in asynchronous futures, as described in A Future for R: Common Issues with Solutions.

The first is around missing globals. Future statically inspects the code before running to figure out what global variables it needs to expose to the R process. When you instantiate a reference class object using new("classname"), the actual class definition isn't known until runtime (when getClass() is called) so future won't know to export it.

A minimal example:

library(future)

plan(multisession)

RefClassTest <- setRefClass("RefClassTest",
  fields = list(x = "numeric"),
  methods = list(get = function() x)
)

result %<-% new("RefClassTest")
result
## Error in getClass(Class, where = topenv(parent.frame())) :
##   "RefClassTest" is not a defined class

A workaround would be to instantiate using the class generator like RefClassTest$new(). However, now you run into an issue exporting the generator since (I guess) it uses external pointers internally. The object isn't quite constructed right.

options(future.globals.onReference = "warning")

result %<-% RefClassTest$new()
## Warning message:
##   In FALSE :
##   Detected a non-exportable reference (‘externalptr’) in one of the globals (‘RefClassTest’ of class ‘refObjectGenerator’) used in the future expression

result
## Prototypical reference class object

result$get()
## Error in result$get() : object 'x' not found

I don't know enough about reference classes to work around both issues, so I would suggest using R6 classes instead. They don't seem to have the same issues as reference classes in future expressions.

R6Test <- R6::R6Class("R6Test",
  public = list(
    x = numeric(0),
    get = function() self$x
  )
)

result %<-% {
  R6Test$new()
}

result
## <R6Test>
##   Public:
##   clone: function (deep = FALSE)
##     get: function ()
##       x:

result$get()
## numeric(0)
like image 76
greg L Avatar answered Sep 28 '22 00:09

greg L