Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Defining methods that call other methods outside of R6 objects

Tags:

oop

r

r6

When using R6 classes, what is the proper way to define methods outside of the class that call other methods?

Consider the following example, where the function func might dispatch to another function if it is being used interactively. But, if it does so, the other function doesn't have access to the private environment. Should I be passing an environment around if I define classes this way?

## General function defined outside R6 class
func <- function(x) {
  if (missing(x) && interactive()) {
    ifunc()
  } else {
    private$a <- x * x
  }
}

## If interactive, redirect to this function
ifunc <- function() {
  f <- switch(menu(c('*', '+')), '1'=`*`, '2'=`+`)
  private$a <- f(private$a, private$a)
}

## R6 test object
Obj <- R6::R6Class("Obj",
  public=list(
    initialize=function(a) private$a <- a,
    geta=function() private$a,
    func=func  # defined in another file
  ),
  private=list(
    a=NA
  )
)

## Testing
tst <- Obj$new(5)
tst$func(3)
tst$geta()  # so func sees 'private'
# [1] 9

tst$func()  # doesn't see 'private'

Error in ifunc() (from #3) : object 'private' not found

like image 873
Rorschach Avatar asked Oct 19 '22 16:10

Rorschach


1 Answers

The problem you are facing is that you define func and ifunc in such a way that the implementation only makes sense inside the class definition - but not completely. You are getting an error because ifunc is implemented as if it knows about the internals of your class (it refers to private), but it doesn't. You never actually include it into your class definition. So you have to refer in func to the private member function ifunc and include ifunc into the class:

func <- function(x) {
    if (missing(x) && interactive()) {
        private$ifunc()
    } else {
        private$a <- x * x
    }
}


ifunc <- function() {
    f <- switch(menu(c('*', '+')), '1'=`*`, '2'=`+`)
    private$a <- f(private$a, private$a)
}


Obj <- R6::R6Class(
    "Obj",
    public=list(
        initialize=function(a) private$a <- a,
        geta=function() private$a,
        func=func  # defined in another file
    ),
    private=list(
        a=NA,
        ifunc=ifunc
    )
)

However, I must say that I don't understand this strategy. The functions func and ifunc then both have their own names in a top level environment but don't work - they really only make sense in the class definition. If code reuse is what you are interested in I think object composition or inheritance would be less surprising.

like image 70
Sebastian Avatar answered Oct 21 '22 06:10

Sebastian