Question

I would like to use delayed assign to assign a new value to a slot of an S4 object. This assignment is basically a database query, and I only want the database query to be executed when the value is actually used.

But for the sake of testing this will do as well:

testFunction <- function(id = 1){
  print("running query")
  return(id)
}

delayedAssign("test", testFunction(id = 2))

This works. "running query" is only printed when test is called, not on assignment. In contrast to:

test2 <- testFunction(id = 2)

Now I would like to be able to do the same thing, but then on a slot.

delayedAssign("someObject@slotName", testFunction(id = 2))

This unfortunately creates an object names 'someObject@slotName' in the current environment. Any ideas on how to solve this?

Was it helpful?

Solution

This represents somewhat of a hack, with unpleasant implementation details. Here's an S4 class, with an environment as a slot

.B <- setClass("B", representation(b="environment"))

I pay attention to initialization, so each instance gets its own environment (rather than all instances sharing the same environment, which would be the default and appropriate if there were a singleton)

setMethod(initialize, "B",
    function(.Object, ..., b=new.env(parent=emptyenv()))
{
    b[["value"]] <- NA
    callNextMethod(.Object, ..., b=b)
})

Let's define generics to set and retrieve the delay-assigned values

setGeneric("delay<-", function(x, ..., value) standardGeneric("delay<-"))
setGeneric("delay", function(x, ...) standardGeneric("delay"))

then implement the method to assign a value to an element, 'value', in our environment

setReplaceMethod("delay", "B", function(x, ..., value) {
    force(value)                        # don't want to be _too_ lazy
    delayedAssign("value", testFunction(value), assign.env=x@b)
    x
})

and to retrieve it

setMethod("delay", "B", function(x, ...) x@b[["value"]])

Here's the product of our labor...

>     b <- .B()
>     delay(b)
[1] NA
>     delay(b) <- 1  # no type safety; could use, e.g., delay<-,numeric-method
>     delay(b)
[1] "running query"
[1] 1

with some weird reference semantics (because b1 and b share the same environment) that would surprise our user (probably even our user expecting reference semantics)

>     b1 <- b         # reference semantics, delayed
>     delay(b1) <- 2
>     delay(b)
[1] "running query"
[1] 2
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top