Closed dkyleward closed 5 years ago
I don't think it's something that should go in R6 itself, but you can do it by defining an S3 method for $
for the Counter
class.
Here's an example that shows one way to do it:
Counter <- R6::R6Class("Counter",
public = list(
do = function(method_name, ...) {
do.call(method_name, list(self, private, ...))
}
),
private = list(
count = 1
)
)
show_count <- function(self, private, ...) {
txt <- paste0("The count is ", private$count, ".")
args <- list(...)
if (length(args) > 0) {
txt <- paste0(
txt,
" Other args: '",
paste(unlist(list(...)), collapse = "', '"),
"'"
)
}
txt
}
obj <- Counter$new()
obj$do("show_count")
#> [1] "The count is 1."
obj$do("show_count", "x", "y", "z")
#> [1] "The count is 1. Other args: 'x', 'y', 'z'"
`$.Counter` <- function(x, name) {
if (exists(name, envir = x)) {
.subset2(x, name)
} else {
function(...) {
.subset2(x, "do")(name, ...)
}
}
}
obj$show_count()
#> [1] "The count is 1."
obj$show_count("x", "y", "z")
#> [1] "The count is 1. Other args: 'x', 'y', 'z'"
Some comments about it:
self
and private
to the function, so that the function can access public and private members of the object..subset2()
works similarly to $
, except it does not do S3 dispatch. The reason for using it here is to index into the object without calling $
, which would dispatch to $.Counter
, resulting in infinite recursion.That's a great solution (and I learned a few things). Thanks for the help!
With this, I can see why R6 wouldn't need a getattr equivalent.
One small update: I realized that instead of this:
if (name %in% names(x))
It's a little better to do this:
if (exists(name, envir = x))
I've changed the example above to reflect that.
This is related to my stackoverflow question here: https://stackoverflow.com/questions/56668510/how-to-handle-unknown-methods-generics-in-r
In short, if you call a member of an R6 object (environment) that doesn't exist, you currently get
NULL
back. I'm wondering if that behavior can be modified.The toy example below shows a "Counter" class that provides its internal count to any function called with the
do
method.Many languages have a way to make this look prettier. What I'd like to be able to use is:
When it doesn't find
show_count()
do ado.call()
. Obviously, this wouldn't be the default behavior of an R6 object, but perhaps a special function could be built into the class that, if included in my class definition, would define what happens when an unknown method/attribute is encountered. In Python, this is the__getattr__
magic method.Thanks for your consideration!