Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Non-standard set-functions in R Reference Classes

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.

like image 596
Anders Ellern Bilgrau Avatar asked Jan 15 '19 20:01

Anders Ellern Bilgrau


People also ask

How are reference classes passed to functions in R?

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.

What is reference class in R?

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.

Are there methods in R?

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 .


2 Answers

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.

like image 117
RolandASc Avatar answered Oct 05 '22 14:10

RolandASc


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"
like image 37
Adam Waring Avatar answered Oct 05 '22 13:10

Adam Waring