0
votes

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?

1
You can only "delayed assign" into environments, not into S4 objects.hadley
@hadley Maybe I should make a request to add this to the language? Or do you think that is way too difficult? And then the question is, how do I go about feature requests like that?wligtenberg
It would be extremely hard to implement because you'd have to be very careful about not accidentally forcing evaluation, when (e.g.) checking if the object is valid.hadley

1 Answers

3
votes

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