简体   繁体   中英

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?

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

I have also found a solution for this simple example class that seems to work - BUT SADLY that solution interestingly failed/fails for more complex R6 classes.

Adding that example solution here for completness

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

# <R6obj> # <- I SUDDENLY NOTICED THIS on a second look! 
#   Public:
#     clone: function (deep = FALSE) 
#     foo: function () 
#    val: 1

S4wR6slot <- function() new("S4wR6slot")

setClass("S4wR6slot", contains = "environment",
         slots = list(a = "R6obj" , b = "character"))
# list(a = "R6obj" ... is what did the "magic"

s4r6 <- S4wR6slot()
s4r6

# An object of class "S4wR6slot"
# <environment: 0x000002520292f470>
# Slot "a":
# NULL
#
# Slot "b":
# character(0)

s4r6@b <- "text"
s4r6@a <- r6o # THIS THEN WORKED/WORKS! 

class(r6o)
# [1] "R6obj" "R6" # I simply used that "R6obj" in the S4 definition above
is(r6o, "R6")
# [1] TRUE
is(r6o, "R6obj")
# [1] TRUE

But somehow that "Robj" class (type) gets lost in more complex settings ie I am using a R6 class defined in one package in another package and then that "R6obj" is no longer available for usage. :-(

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM