user2968765
user2968765

Reputation: 155

setClass and setMethod wrappers used in a package: What is the correct assignment environment?

I have a preprocessing package with a wrapper function that helps the user to use inheritance. These user-defined classes and methods can not be stored to the sealed package namespace as I have done with top-level code default classes and methods. What is the correct environment to assign the definitions into? The solution below seems to work somehow but I don't understand it well enough.

setpreprocessor <- function(classname, operation, mode="numeric"){

setClass(classname, contains="PreprocessorClass", 
where=topenv(parent.frame()), prototype=prototype(objectname=classname,  
objectoperation=operation))

setMethod("transformdata", where=topenv(parent.frame()), signature(object = 
classname), function(object, dataobject) {

...code here uses arguments "operation" and "mode"...
})
}

Upvotes: 2

Views: 266

Answers (1)

Martin Morgan
Martin Morgan

Reputation: 46866

Here's a more complete example, with some formatting to make the structure of the code a little more apparent

setClass("PreprocessorClass")

setGeneric("transformdata",
           function(object, dataobject) standardGeneric("transformdata"))

setpreprocessor <- function(classname, operation, mode="numeric") {

    setClass(classname, contains="PreprocessorClass", 
             where=topenv(parent.frame()),
             prototype=prototype(objectname=classname,  
               objectoperation=operation))

    setMethod("transformdata", signature(object = classname),
              where=topenv(parent.frame()),
              function(object, dataobject) {
                  ## code that uses 'operation', 'mode'
                  list(operation, mode)
              })
} 

parent.frame() is the environment from which the function is called (not the environment in which the function is defined).

Almost all environments have an enclosing environment, typically the environment in which the environment itself was defined; see ?parent.env. topenv() starts from the specified argument, and traces enclosing environments of each environment until it reaches .GlobalEnv or a package namespace.

So if your code were in a package PkgA, the user loaded the package, and then invoked setpreprocessor("Foo", "bar") from the global environment (command prompt), parent.frame() would return .GlobalEnv, as would topenv(). You would see

> ls(all=TTRUE)
[1] ".__C__Foo" ".__T__transformdata:PkgA"

which are the class and method definitions created in the global environment.

On the other hand, if the user used setpreprocessor() in a function in a package, topenv(parent.frame()) would end up at the (sealed) package namespace. Because the package namespace is sealed, it would not be possible to create the class or method definitions.

An alternative is to provide an environment into which the class and method definitions can be cached

.PreprocessorCache <- new.env()

setClass("PreprocessorClass")
## ...

and then use

where=.PreprocessorCache

in the class and method definitions. setpreprocessor() can then be invoked either interactively or in package code.

Upvotes: 3

Related Questions