10

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.

Anders Ellern Bilgrau
  • 9,498
  • 1
  • 28
  • 37
  • I would think the "particular name" for such functions would be "assignment". You should also realize that `$ – IRTFM Jan 15 '19 at 21:45
  • @42- Yeah, but assignment would usually refer to the `assign`-function or ` – Anders Ellern Bilgrau Jan 15 '19 at 21:55
  • I think searching the documentation files would be more likely to return a manageable number of hits. – IRTFM Jan 15 '19 at 21:58
  • @42- Thanks. I tried once again (following the docs for `[[ – Anders Ellern Bilgrau Jan 15 '19 at 22:09
  • So "named assignment" or "assignment by character index" or perhaps "sub-assignment" might trigger the appropriate set of neurons in someone who's more conversant with Reference class functions on Rhelp or R-devel than am I. – IRTFM Jan 15 '19 at 22:13
  • I guess that this has little to do with reference classes and it doesn't work for the same reason `names(1:10) – nicola Feb 09 '19 at 05:42
  • @Nicola Thank you for the comment. I see what you mean, but I cannot help for feel this is not quite the same because the first argument refers to a existing object that is mallable (while not an assigned 'object' itself I know). But I guess you are right as `'names – Anders Ellern Bilgrau Feb 10 '19 at 09:45

2 Answers2

3

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"
Adam Waring
  • 1,008
  • 8
  • 19
  • Well, there nothing wrong with it in itself. But the project I'm working prefer get and set functions and not manipulating fields directly. Secondly, the use case is not as simple as the one above. The fields are more complicated data structures where something 'special' needs to be done. Lastly, I just find assignment-functions (`myfunction(x) – Anders Ellern Bilgrau Feb 10 '19 at 09:28
3

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.

RolandASc
  • 3,764
  • 1
  • 9
  • 26
  • Thanks for the answer. Although I still don't understand *why* it does not work, I'm starting to accept it indeed is impossible. I definitely like your suggestion for the alternative syntax. But maybe I'll just have live with a standard reference class assignment function---I'm not sure I want to bring the S3 system into this and add another layer of complexity. This *is* using the S3 system, right? In any case, if no other answers comes closer to my wanted syntax I'll award you the answer. – Anders Ellern Bilgrau Feb 11 '19 at 19:08
  • Correct, this is ultimately using S3. Just note that the `setGeneric` and `setMethod` will typically pop up in the context of S4 first. – RolandASc Feb 12 '19 at 12:40