Proper way to implement S3 dispatch on R6 classes - r

I have an R6 class and I want to add an S3 method for it. The documentation I found mentioned briefly that in order to use S3 dispatch on R6 you must have class = TRUE, but I couldn't find an example of how it should be done.
I did see empirically that simply writing an S3 method in the form s3generic.r6class worked, but I wanted to know if that is indeed to right way to write an S3 method for R6.
For example, say I have an R6 class that enhances a list
library(R6)
R6list <- R6Class(
"R6list",
public = list(
orig = NULL,
initialize = function(x) {
self$orig <- x
}
)
)
Question 1
Naturally, I want to provide a method for obtaining the underlying list, so I wanted to add an as.list method. Is it standard to add both an S3 generic AND a as.list public function inside the class? My intuitive answer is to add both.
R6list <- R6Class(
"R6list",
public = list(
orig = NULL,
initialize = function(x) {
self$orig <- x
},
as.list = function() {
self$orig
}
)
)
as.list.R6list <- function(x, ...) {
x$as.list()
}
So that now if I have an object mylist <- R6list$new(as.list(letters[1:5])) I can either call as.list(mylist) or mylist$as.list(). Is one of those preferred over the other?
Question 2
Is there anything special about writing an S3 method for R6 classes, or is what I wrote above sufficient and the correct way? I wasn't sure if the S3 method has to be written outside of the class definition, or if R6 somehow provides a way to write S3 methods within it so that all the code relating to the class is localized.

I asked Winston Chang, the author of R6, about this on Github. According to him, the code provided in Question 1 above is the correct way of writing S3 methods for R6 classes.

Related

Unable to access object inside function using an external function R6

I have a R6 class with code like this
# Cutting out lots of code and only putting in relevant lines
public(
function1 <- function(){
var <- xyz$abc
},
function2 <- function(){
xyz <- blah blah
z <- function1()
}
)
When calling function2 I get an error in function1 saying that xyz is not found even though its assigned in function2 which is called before function1
Please let me know if I am understanding this correctly and how to fix it.
For "traditional" R functions the parent of the evaluation environment of a function is the calling environment.
For R6 function this not the same. The parent of the evaluation environment of a method is an environment enclosing the self variable that gives access to object properties.
You can test this by adding
print(ls(parent.env(environment()))) in your method.
This means that you can't have access to your xyz variable in function1. You must use public or private variables or pass it as a parameter to your function.
By the way you must also prepend self$ to the call of function1 (self$function1())

s4 classes exported by default if presence of an initialize method

I thought classes needed an exportClass directive in NAMESPACE to be exported,
but
Classes defined in a package but not exported are nevertheless exported if there is an initialize method for the class in the code base.
that is to say I can create an instance of the class event though I exported nothing.
so
setClass("example", slots = c( title = "string"))
setClass("example2", slots = c( title = "string"))
and
setMethod("initialize","example, function(.Object, title) {
.Object#title <- title
. Object
})
in the package R directory and NAMESPACE present but with no export directives in it,
results in the possibility to create an instance of the class
library(example_package)
new("example") # ok
new("example2") # fails
I guess this is because initialize is a generics that is already defined
but does it mean you cannot prevent class instantiation by the user of the package if there is an initialize method for the class ?
or maybe put the initialize inside the setClass instruction ?

Difference between self and private method call in R6

Lately I found myself coding some stuff in R6, and while having fun handling some objects an interesting question came out. When creating a private method (e.g. foo, of the bar) and calling it inside other public methods it works if I either call it using:
self$foo
and
private$foo
What I am asking is: what is the difference between those two ways of calling a method in R6?
Thank you in advance!
From the "Introduction to R6 classes" vignette (emphasis mine):
self
Inside methods of the class, self refers to the object. Public members of the object (all you’ve seen so far) are accessed with self$x
private
Whereas public members are accessed with self, like self$add(), private members are accessed with private, like private$queue.
So, even if you can access private methods through self, you should do it through private. Don't rely on behavior which may go away, seeing as how the documentation says it shouldn't work like that.
That said, I can't access private methods using self:
library(R6)
bar <- R6Class("bar",
private = list(
foo = function() {
message("just foo it")
}
),
public = list(
call_self = function() {
self$foo()
},
call_private = function() {
private$foo()
}
)
)
b <- bar$new()
b$call_private()
# just foo it
b$call_self()
# Error in b$call_self() : attempt to apply non-function

Reference class inheritance from external package

I am trying to override a reference class method. Because reference class methods are bound to the class rather than the object, I believe in order to do this I need to define a new reference class which inherits from the old reference class. However the class I am trying to inherit from is defined in an external package to mine (dplyr). I cannot figure out the correct syntax to do this, contains seems to require only a text name, and does not search for class definitions in external packages.
In particular I am trying to inherit from the DbDisconnector reference class from dplyr and override the finalize method.
This correctly finds the parent class, but then cannot assign to it as it is from a different package.
NewDbDisconnector <- setRefClass("NewDbDisconnector",
contains = 'DbDisconnector',
methods = list(
finalize = function() {
message("test")
}
),
where=getNamespace('dplyr')
)
# Error in assign(mname, def, where) (from file.r#75) :
# cannot add bindings to a locked environment
Contains methods only accept strings, they cannot just be given a refClass definition from getRefClass.
NewDbDisconnector <- setRefClass("NewDbDisconnector",
contains = getRefClass("DbDisconnector", getNamespace("dplyr")),
methods = list(
finalize = function() {
message("test")
}
)
)
# Error in FUN(X[[1L]], ...) :
# the 'contains' argument should be the names of superclasses: got an element of class “name”
I would think this should be possible, I just cannot figure out the correct way to do it.
You can import the superclass to your environment:
DbDisconnector <- getFromNamespace("DbDisconnector", "dplyr")
And then set contains = "DbDisconnector" in your class.

how can i invoke ClassB from ClassA in R language, ClassA and ClassB are in different file

I am a newbie in R language, so I think i should illustrate this requirement just as Java
now we know R can support the OOP, so I want to invoke ClassB method from ClassA
ClassB.java
public class ClassB {
public void printB() {
System.out.println("a");
}
}
ClassA.java
//if they are in the same package, use it directly
//or we must import ClassB explicitly
public class ClassA {
public void invokeB() {
ClassB b = new ClassB();
b.printB();
}
}
so how can i achieve this in R language?
This is how assignment of classes is done in the S3 system of method dispatch. It's not really OOP as Java users understand it. The proto package provides a framework that more closely resembles Java style OOP. The S4 system of method dispatch provided multi-argument signature matching and a more discipline approach... that most R-users cannot understand.
newItem <- "bbb"
class(newItem) <- "newClass"
print(newItem)
#[1] "bbb"
#attr(,"class")
#[1] "newClass"
print.newClass <- function(n) cat("this is of the newClass", n)
print(newItem)
#this is of the newClass bbb
otherItem <- "xxxx"
inherits(otherItem, "newClass")
#[1] FALSE
class(otherItem) <- c( class(otherItem), "newClass")
print(otherItem)
#this is of the newClass xxxx
When you talk about "classes" being in a "different file", then you probably need to study package construction and the care and feeding of NAMESPACEs. You are able to attach packages as well as load via namespaces. R functions are perhaps somewhat similar to Java classes (in a rather loose use of the English language, but not either Java or R) but in R, the term 'class' refers to an attribute of an object which is used to dispatch functions.

Resources