Search code examples
rclassr-s4r6

Is there a way/method to assign a R6 object to an S4 object slot?


As the topic describes I am looking for a way to achieve this:

require("R6")
R6cls <- R6::R6Class("R6obj",
    public = list(val = 1, foo = function() "foo!")
)
# check this R6 class
r6o <- R6cls$new()
r6o

# <R6obj>
#  Public:
#    clone: function (deep = FALSE) 
#    foo: function () 
#    val: 1

# now the S4 part 
S4wR6slot <- function() new("S4wR6slot")
setClass("S4wR6slot", contains = "environment", # or maybe "R6" or "R6cls"? tried them all but none seemed to work
         slots = list(a = "R6cls", b = "character"))
   
# Currently gives
Warning message:
undefined slot classes in definition of "S4wR6slot": a(class "R6cls") 
# but you can still kind of use it 
s4r6 <- S4wR6slot()
s4r6

# An object of class "S4wR6slot"
# <environment: 0x00000179de2da628>
# Slot "a":
# NULL
#
# Slot "b":
# character(0)
s4r6@b <- "text" # works nicely 
# BUT trying to assign to that env slot 
s4r6@a <- r6o

# will throw these "ugly" errors 
Error in (function (cl, name, valueClass)  : 
  c("assignment of an object of class “R6obj” is not valid for @‘a’ in an object of class “S4wR6slot”; is(value, \"R6cls\") is not TRUE", 
"assignment of an object of class “R6” is not valid for @‘a’ in an object of class “S4wR6slot”; is(value, \"R6cls\") is not TRUE")

Playing around with setOldClass() also did no good. Maybe there is simply no way to make that work - just asking if someone has an idea? Or maybe even achieved this - otherwise maybe I did not rtfm well enough and one can point me to the documentation, where it says thou' shall not try to make this work?


Solution

  • There is a way to have an R6 object inside an S4 class, but involves tricking the S4 system by defining an S4 class called 'R6'. This will prevent the methods package complaining that class R6 doesn't exist. Fortunately, it doesn't actually check whether the 'R6' object in the prototype is of the S4 type, allowing you to stick an actual R6 object in there.

    library(R6)
    
    R6cls <- R6::R6Class("R6obj",
                         public = list(val = 1, foo = function() "foo!")
    )
    
    setClass('R6')
    
    setClass("S4wR6slot", slots = list(a = 'R6', b = "character"),
             prototype = list(a = R6cls$new(), b = 'Hello'))
    
    S4wR6slot <- function() new("S4wR6slot")
    
    myS4 <- S4wR6slot()
    
    class(myS4)
    #> [1] "S4wR6slot"
    #> attr(,"package")
    #> [1] ".GlobalEnv"
    
    isS4(myS4)
    #> [1] TRUE
    
    myS4@a
    #> <R6obj>
    #>   Public:
    #>   clone: function (deep = FALSE) 
    #>     foo: function () 
    #>       val: 1