3

I have a class 'myClass' in R that is essentially a list. It has an assignment operator which is going to do some things and then should assign the value using the regular list assignment operator

`$<-.myClass`<-function(x,i,value){
   # do some pre-processing  stuff

   # make the assignment using the default list assignment
   x[[i]]<-value
   x
 }

But I can't actually use x[[i]]<-value as it will dispatch to the already existing [[<-.myClass method.

In similar S3 dispatching cases, I've been able use UseMethod or specifically call [[<-.list, or [[<-.default but those don't seem to exist because $<- and [[<- are primitive generics, right? And I'm sure I'll be sent to a special R hell if I try to call .Primitive("$<-"). What is the correct way to dispatch the assignment to the default assignment method?

skyebend
  • 1,079
  • 6
  • 19
  • 1
    Have you actually tried it? `$<-` is not the same as `[[<-`. – Hong Ooi Dec 17 '13 at 07:45
  • Have you tried using `NextMethod()`? That would be the obvious place to start. – hadley Dec 17 '13 at 13:58
  • Except that $ does non-standard evaluation, so this is going to be hard – hadley Dec 17 '13 at 15:56
  • I have tried NextMethod, but maybe I wasn't calling it correctly? Current solution is to remove the class from x, call [[x]]<-value, and re-add the class. But this is expensive because it causes three copies of X to be made. Seems like it should be possible to tell NextMethod to dispatch to 'no class at all' but I couldn't figure it out. – skyebend Dec 17 '13 at 16:05

1 Answers1

2

It doesn't look like there is a particularly elegant way to do this. The data.frame method for $<- looks like this:

`$<-.data.frame` <- function (x, name, value) {
  cl <- oldClass(x)
  class(x) <- NULL
  x[[name]] <- value
  class(x) <- cl
  x
}

(with error checking code omitted)

This should only create one copy of x, because class<- modifies in place, and so does the default method for [[<-.

hadley
  • 102,019
  • 32
  • 183
  • 245
  • > myFrame<-data.frame() > myFrame<-data.frame(a="a") > tracemem(myFrame) [1] "<0xb3889e8>" myFrame[['a']]<-1 tracemem[0xb21a8e8 -> 0xaf37df0]: tracemem[0xaf37df0 -> 0xaf38110]: [[<-.data.frame [[<- tracemem[0xaf38110 -> 0xaf38490]: [[<-.data.frame [[<- Doesn't this mean multiple copies are made? – skyebend Dec 17 '13 at 16:11
  • @skyebend, When you set the `class` to `NULL`, `x` will remain a NAMED list, and `[[<-` will create a copy. See http://stackoverflow.com/questions/18424626/why-is-preallocation-useful-for-list for some examples of when copying on lists might be taking place. – mnel Dec 17 '13 at 22:28