[R] S4 pass-by-value work-around?

Martin Morgan mtmorgan at fhcrc.org
Thu Jun 19 19:00:21 CEST 2008


Hi again Jeff...

Jeffrey Spies <jspies2008 at gmail.com> writes:

> Thanks for the response, Martin.  While the solutions offered update the
> object appropriately, we wouldn't get the desired return value (a string
> followed by the counter, "unique_1") when the methods are called.  Do you
> know a way of dealing with this?

I don't have a solution for you, other than calling a method to
increment the counter and a second to retrieve the current name.

Here's another unsatisfactory solution...

setClass("Stateful",
         representation=representation(
           state="environment"))

setMethod("initialize", "Stateful",
          function(.Object, ..., state=new.env(parent=emptyenv())) {
              state[["count"]] <- 0
              callNextMethod(.Object, ..., state=state)
          })

setGeneric("state", function(x) standardGeneric("state"))

setMethod("state", "Stateful", function(x) x at state)

setGeneric("uname", function(x, ...) standardGeneric("uname"))

setMethod("uname", "Stateful", function(x, ...) {
    state <- state(x)
    assign("count", state[["count"]] + 1, state)
    paste("unique", state[["count"]], sep="_")
})


> s <- new("Stateful")
> uname(s)
[1] "unique_1"
> uname(s)
[1] "unique_2"

... and the reason why this will be surprising to users

> s <- t <- new("Stateful")
> uname(s)
[1] "unique_1"
> uname(s)
[1] "unique_2"
> uname(t) # hey, this is the first time I referenced t!
[1] "unique_3"

I guess this points to maybe some subtle issues with what you're
trying to do, arising from a tension between pass-by-value and
pass-by-reference (or to my own fuzzy-headed thinking). For instance,
supppose you've implemented your 'uniqueName' function to update the
object but return the unique name, and you've done it without using an
environment like above, so that each instance of MyMatrix has its own
counter.  Say you have an instance x of 'MyMatrix' in the global
environment. You pass it to a function. R's pass-by-value means it
gets copied to a local variable in the function, say x1.  The function
queries 'uniqueName', modifying x1. But x in the global environment is
unchanged. So the next time this function is invoked, it gets the same
'unique' name. Is this what you were expecting?

These are just my two cents, of course, maybe others have different
ideas.

Martin


> Jeff.
>
>
> Martin Morgan wrote:
>> 
>> Hi Jeff --
>> 
>> two different scenarios are to overwrite the current object, along the
>> lines of
>> 
>> y <- uniquify(y)
>> 
>> where uniquify is a method like createUniqueName but returns the
>> (modified) instance rather than unique name
>> 
>> setMethod('uniquify', 'MyMatrix', function(x) {
>>     x at uniqueCount <- # something unique
>>     x
>> })
>> 
>> The second is a replacement method, along the lines of
>> 
>> setGeneric("uniqueCount<-",
>>     function(x, ..., value) standardGeneric("uniqueCount<-"))
>> 
>> setReplaceMethod("uniqueCount",
>>     signature=c(x="MyMatrix", value="numeric"),
>>     function(x, ..., value) {
>>         x at uniqueCount <- value
>>         x
>>     })
>> 
>> uniqueCount(x) <- uniqueCount(x) + 1
>> x # now modified
>> 
>> This is untested psuedo-code, so I hope it's right enough to get you
>> going.
>> 
>> Martin
>> 
>> Jeffrey Spies <jspies2008 at gmail.com> writes:
>> 
>>> Howdy all,
>>>
>>> I have a problem that I'd like some advice/help in solving---it has to do
>>> with R's pass-by-value system.  I understand the issue, but am wondering
>>> if
>>> anyone has found a working solution in dealing with it for cases when one
>>> wants to modify an object inside of a method, specifically when working
>>> with
>>> S4.  I'm aware that R.oo is able to deal with this using S3, but I'd
>>> really
>>> rather stick to S4.
>>>
>>> The basics of what I would like to do are coded below:
>>>
>>> setClass("MyMatrix",
>>> 	representation(
>>> 		parameters="matrix",
>>> 		uniqueCount="numeric"
>>> 	),
>>> 	prototype(
>>> 		parameters=matrix(numeric(0),0,0),
>>> 		uniqueCount=1	
>>> 	)
>>> )
>>>
>>> setGeneric("createUniqueName", function(object)
>>> standardGeneric("createUniqueName"))
>>>
>>> setMethod("createUniqueName", "MyMatrix", function(object){
>>> 	retval <- paste("unique_", object at uniqueCount, sep="")
>>> 	object at uniqueCount <- object at uniqueCount + 1
>>> 	return(retval)
>>> })
>>>
>>> x <- new("MyMatrix", parameters=matrix(0, 2, 2))
>>> createUniqueName(x) # returns "unique_1"
>>> x # x at uniqueCount is still 1
>>>
>>> I understand why this is happening, but am wondering how people in the
>>> community have dealt with it, specifically when using S4.  Any advice
>>> would
>>> be appreciated.  Also, I am aware that this is somewhat of a silly
>>> example,
>>> but it should allow you to see what I'm trying to accomplish.
>>>
>>> Thank you,
>>>
>>> Jeff.
>>>
>> 
>
> -- 
> View this message in context: http://www.nabble.com/S4-pass-by-value-work-around--tp17997553p18012246.html
> Sent from the R help mailing list archive at Nabble.com.
>
> ______________________________________________
> R-help at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
> and provide commented, minimal, self-contained, reproducible code.

-- 
Martin Morgan
Computational Biology / Fred Hutchinson Cancer Research Center
1100 Fairview Ave. N.
PO Box 19024 Seattle, WA 98109

Location: Arnold Building M2 B169
Phone: (206) 667-2793



More information about the R-help mailing list