How can I prevent R6 classes to access non-existent members?

141 Views Asked by At

R6 automatically sets to NULL anything I access on self

> x <- R6::R6Class("x", list(foo = function() { print(self$notexistent) }))
> xx <- x$new()
> xx$foo()
NULL

This means that if I make a typo in one access e.g. results instead of result it will use NULL instead of throwing an error. Is there a way to force the latter?

1

There are 1 best solutions below

0
On BEST ANSWER

The self in an R6 function is an environment. The $ operator for environments is a primitive, and as far as I can tell it can't be overwritten. By default this operator returns NULL rather than throwing an error if the right-sided operand isn't found in the environment, and this fact has to be explicitly handled by the caller.

This isn't so different from, say, the [ operator in C++, which doesn't bounds check a vector. Unless you explicitly check your bounds, your program will crash. If you want to bounds check you need to use a different operator like .at: these will be safer, but will also slow your software down. It boils down to priorities. You either go fast or you change syntax and check.

Therefore if you would prefer bounds-checking behaviour from self, you need to write a different, safer accessor with different syntax.

`%$%` <- function(env, member) {
  member <- deparse(substitute(member))
  ls <- eval(as.call(list(quote(ls), substitute(env))), 
                     envir = parent.frame())
  if(!member %in% ls) stop(member, " is not a member of this R6 class")
  eval(as.call(list(quote(`[[`), substitute(env), member)), 
       envir = parent.frame())
}

x <- R6::R6Class("x", list(foo = function() print(self%$%notexistent),
                           bar = function() print(self%$%baz),
                           baz = 1))
xx <- x$new()

xx$foo()
#> Error in self %$% notexistent : 
#>   notexistent is not a member of this R6 class 

xx$bar()
#> [1] 1

You may not like the fact that you can't use the $ operator in this way, but that's the way R is. I think it's clear you don't like the language, and I can't change that, but until your plans to destroy it are realised, you can see it as a challenge...