R6 pass a self$FUN as parameter

221 Views Asked by At

I am starting to built a "user-friendly" R6 class and want to make a function does most of the work of my class. This is my structure so far

x <- X$new()
veggie_cubes <- veggie %>% x$cubesX(ID)
veggie_slices <- veggie %>% x$sliceX(ID)

My question is now if it is possible to rewrite the code such that:

x <- X$new()
veggie_cubes <- veggie %>% x$cutX(cubesX, ID)
veggie_slices <- veggie %>% x$cutX(sliceX, ID)

The function header should somehow look like: cut(.data, FUN, KEY)

So far my idea was to write cut like:

cutX= function(.data, FUN, KEY)
{
   .data %>%
     FUN({{ KEY }}) %>%
     base::return()
}

The only way this works was by calling veggie %>% x$cutX(x$cubesX, ID) which I would not really prefer as "user-friendly" solution, I also do not really like to use strings for that. Is there a way to write it without the x$?

here is the simplified R6 class:

X <- R6::R6Class(
  classname = "X",
  public = base::list(
    cubesX = function(.data, KEY)
    {
      .data %>% 
        dplyr::select(!{{ KEY }}) %>% 
        base::return()
    },
    sliceX = function(.data, KEY)
    {
      .data %>% 
        dplyr::select({{ KEY }}) %>% 
        base::return()
    },
    cutX = function(.data, FUN, KEY)
    {
      .data %>% 
        FUN( {{ KEY}}) %>% 
        base::return()
    }
  )
)

running examples:

x <- X$new()
iris %>% x$sliceX(Species)
iris %>% x$cubesX(Species)
# with FUN
iris %>% x$cutX(x$sliceX, Species)
iris %>% x$cutX(x$cubesX, Species)

not running:

iris %>% x$cutX(sliceX, Species)
iris %>% x$cutX(cubesX, Species)

Thanks in Advance :-)

1

There are 1 best solutions below

0
On BEST ANSWER

The following implementation will allow you to call your functions via any of your three methods. It works by using non-standard evaluation to figure out whether FUN is in the format x$func or just a bare func. In either case it takes the bare function name and builds a call to turn it into self$func. Then it just evaluates that function.

The following R6 class therefore works as expected on all your test examples:

X <- R6::R6Class(
  classname = "X",
  public = base::list(
    cubesX = function(.data, KEY)
    {
      .data %>% 
        dplyr::select(!{{ KEY }}) %>% 
        base::return()
    },
    sliceX = function(.data, KEY)
    {
      .data %>% 
        dplyr::select({{ KEY }}) %>% 
        base::return()
    },
    cutX = function(.data, FUN, KEY)
    {
      if(is.call(substitute(FUN))) {
        FUN <- substitute(FUN) 
        FUN[[2]] <- quote(self)
      }
      else 
        FUN <- as.call(list(quote(`$`), quote(self), substitute(FUN)))
 
      eval(as.call(list(FUN, quote(.data), substitute(KEY))))
    }
  )
)

So, for example:

x <- X$new()
iris %>% x$cutX(sliceX, Species)

#>        Species
#> 1       setosa
#> 2       setosa
#> 3       setosa
#> 4       setosa
#> 5       setosa
#> 6       setosa
#> 7       setosa
#> 8       setosa
#> 9       setosa
#> 10      setosa
#> 11      setosa
#> 12      setosa
#> 13      setosa
#> 14      setosa
#> 15      setosa
#> 16      setosa
#> 17      setosa
#> 18      setosa
#> 19      setosa
#> 20      setosa
#> ...etc