Let's say I have a constructor of a class with two properties with one initiated and the other set to NULL
:
myclass <- function(data) {
structure(
list(data1 = data,
data2 = NULL),
class = "myclass")
}
And a generic:
myadd <- function(obj, x) {
UseMethod("myadd")
}
myadd.myclass <- function(obj, x) {
obj$data2 = obj$data1 + x
}
When I do:
mc = myclass(1)
myadd(mc, 2)
The property data2
does not change:
> mc
$data1
[1] 1
$data2
NULL
attr(,"class")
[1] "myclass"
Obviously, when I assign the result to a variable:
tmp = myadd(mc, 2)
I get the result:
> tmp
[1] 3
How to modify the property of an existing object with a generic function? Is it even kosher to do so?
I'm guessing I'm missing some crucial piece of info about S3 classes in R or about OOP in general. Any tips appreciated.
1) pass it back R makes a copy of obj
in the function when there is an attempt to modify it. The original obj
is not changed. The idea is that R is a functional language and that minimizes side effects.
Pass it back to the caller and assign it in the caller.
myadd.myclass <- function(obj, x) {
obj$data2 = obj$data1 + x
obj
}
mc <- myclass(1)
mc <- myadd(mc, 2)
mc
## $data1
## [1] 1
##
## $data2
## [1] 3
##
## attr(,"class")
## [1] "myclass"
2) replacement method Another possibility is to define a replacement function:
"myadd<-" <- function(x, ..., value) UseMethod("myadd<-")
"myadd<-.myclass" <- function(x, ..., value) { x$data2 <- x$data1 + value; x }
mc <- myclass(1)
myadd(mc) <- 2
mc
## $data1
## [1] 1
##
## $data2
## [1] 3
##
## attr(,"class")
## [1] "myclass"
3) environment Yet another approach is to use an environment rather than a list.
myclass <- function(data) {
structure(local({
data1 = data
data2 = NULL
environment()
}), class = c("myclass", "environment"))
}
# next two functions are same as in question
myadd <- function(obj, x) UseMethod("myadd")
myadd.myclass <- function(obj, x) obj$data2 = obj$data1 + x
obj <- myclass(1)
myadd(obj, 2)
as.list(obj)
## $data1
## [1] 1
##
## $data2
## [1] 3