Is it possible to get the syntax
foo$bar(x) <- value
to work where foo
is a reference class object and bar
is a method?
I.e. is it possible to do "subset assigment" and have "replacement functions" as methods in Reference Classes?
Is the syntax possible to get with other OO systems?
Example:
I'll illustrate with a made-up use case. Imagine a reference class, Person
, which contains some basic information of a person. Particularly, one field called fullname
is a named list
:
PersonRCGen <- setRefClass("Person",
fields = list(
fullname = "list",
gender = "character"
))
Next, we should define some methods to get and set particular names within the fullnames
list which (try) to give the above syntax/interface. My best attempt has so far been:
PersonRCGen$methods(
name = function(x) { # x is the dataset,
.self$fullname[[x]]
},
`name<-` = function(x, value) {
.self$fullname[[x]] <- value
}
)
The naming here should also illustrate what I'm trying to do.
We initialize a new object:
a_person <- PersonRCGen$new(fullname = list(first = "Jane", last = "Doe"),
gender = "F")
Accessing the fullname
field directly and accessing the first and last name by the defined get-function works as intended:
a_person$fullname
#$`first`
#[1] "Jane"
#
#$last
#[1] "Doe"
a_person$name("first")
#[1] "Jane"
a_person$name("last")
#[1] "Doe"
However, for setting a particular name in the fullname
list, I'd like to have the following syntax/interface which unfortuantely fails.
a_person$name("first") <- "Jessie"
#Error in a_person$name("first") <- "Jessie" :
# target of assignment expands to non-language object
I know the following works (which basically renders the method poorly named).
a_person$`name<-`("first", "Johnny")
a_person$fullname
#$`first`
#[1] "Johnny"
#
#$last
#[1] "Doe"
In my real use case, I'd like to avoid 'traditional' getName(x)
and setName(x, value)
names for the get and set functions.
Inheritance. Reference classes inherit from other reference classes by using the standard R inheritance; that is, by including the superclasses in the contains= argument when creating the new class. The names of the reference superclasses are in slot refSuperClasses of the class definition.
Reference class in R programming is similar to the object oriented programming we are used to seeing in common languages like C++, Java, Python etc. Unlike S3 and S4 classes, methods belong to class rather than generic functions. Reference class are internally implemented as S4 classes with an environment added to it.
R possesses a simple generic function mechanism which can be used for an object-oriented style of programming. Method dispatch takes place based on the class(es) of the first argument to the generic function or of the object supplied as an argument to UseMethod or NextMethod .
I don't think you can do this with your desired syntax.
Note that you will get the same error if you run any assignment like that, e.g.
a_person$hello("first") <- "John"
so it's really a basic problem.
What does work, is the following syntax:
name(a_person, "first") <- "John"
Altogether you could then have something like below:
PersonRCGen <- setRefClass("Person",
fields = list(
fullname = "list",
gender = "character"
),
methods = list(
initialize = function(...) {
initFields(...)
},
name = function(x) {
.self$fullname[[x]]
}
)
)
setGeneric("name<-", function(x, y, value) standardGeneric("name<-"))
setMethod("name<-", sig = "ANY", function(x, y, value) {
UseMethod("name<-")
})
# some extras
"name<-.default" <- function(x, y, value) {
stop(paste("name assignment (name<-) method not defined for class", class(x)))
}
"name<-.list" <- function(x, y, value) {
x[[y]] <- value
return(x)
}
# and here specifically
"name<-.Person" <- function(x, y, value) {
x$fullname[[y]] <- value
return(x)
}
# example to make use of the above
a_person <- PersonRCGen$new(
fullname = list(
first = "Jane",
last = "Doe"
),
gender = "F"
)
a_person$name("first")
#> [1] "Jane"
name(a_person, "middle") <- "X."
a_person$name("middle")
#> [1] "X."
I'm aware this is not exactly what you want but I hope it helps.
I am probably misunderstanding what you are trying to achieve but what's wrong with this?
person = setRefClass("Person",
fields = list(
fullname = "list",
gender = "character"
))
a_person = person$new(fullname = list(first = "James", last = "Brown"), gender="M")
a_person$fullname$first = "Bob"
a_person
Reference class object of class "Person"
Field "fullname":
$`first`
[1] "Bob"
$last
[1] "Brown"
Field "gender":
[1] "M"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With