Hello, I would like to create a function that gets passed a class name and then calls setClass, and a few other functions, inside. I have done this in the past with setmethod, creating accessors for all slots in a set of S4 classes. But setClass is choking when my function is called isnide a package, telling about an error in exists(cname, where). I assume this to be a problem with the environment where the class is created. If I define my creator function in a package and load it, when I call it I get Error in assign(mname, def, where): cannot add bindings to a locked environment This does not happen if I redefine the creator function in my working environment. I have attached an example of the kind of function below, and I am using R 2.5.0. Where should I look for information on undertanding what might be going on here? Why does this way of working succeeds with setMethod but not setClass? The intention here is to provide an easy way for creating extended classes that will inherit from a class already defined, instead of array as in the example. Many thanks, Iago defineClass <- function(name, dimnames) { # dim, names[2], dimnames[2] if(names(dimnames)[1] != 'iter') dimnames <- c(list(iter=1), dimnames) dim <- unlist(lapply(dimnames, length)) # validity foo <- "validity <- function(object) {" # foo <- c(foo, "browser()") foo <- c(foo, paste("if(all(names(dimnames(object)) != c('", paste(names(dimnames), collapse="','", sep=""), "')))", sep="")) foo <- c(foo, "stop(paste('dimnames are not correct for class', name))") foo <- c(foo, "return(TRUE)}") eval(parse(text=foo)) # setClass setClass(name, representation('array'), prototype=prototype(array(as.numeric(NA), dim=dim, dimnames=dimnames), units='NA'), validity=validity, where=sys.parent(n=-2)) # setValidity setValidity(name, validity) # constructors eval(parse(text=paste("setGeneric('", name, "', function(object, ...) standardGeneric('", name, "'))", sep=""))) setMethod(name, signature(object='ANY'), function(object, ...) return(FLPar(object, ..., class=name)) ) setMethod(name, signature(object='missing'), function(...) { return(FLPar(..., class=name)) } ) } --- Iago Mosqueira Systems Modelling Cefas Pakefield Rd. Lowestoft NR33 0HT U.K. Tel +44 (0) 1502 558003 *********************************************************************************** This email and any attachments are intended for the named re...{{dropped}}
Hi Iago -- Here's my attempt at an answer. "Iago Mosqueira (Cefas)" <Iago.Mosqueira at cefas.co.uk> writes:> Hello, > > I would like to create a function that gets passed a class name and > then calls setClass, and a few other functions, inside. I have done > this in the past with setmethod, creating accessors for all slots in a > set of S4 classes. But setClass is choking when my function is called > isnide a package, telling about an error in exists(cname, where). I > assume this to be a problem with the environment where the class is > created. If I define my creator function in a package and load it, > when I call it I get > > Error in assign(mname, def, where): cannot add bindings to a locked > environmentYou're trying to create a class in the package name space, and the name space is locked (cannot be modified) after the package is loaded. If your intention is programmatically generate static classes (in a kind of macro-like way) then I think you could still use this approach but ensure that the classes are created when the package is loaded (e.g., by including a class creation function that is actually evaluated, not just defined, in your package code).> This does not happen if I redefine the creator function in my working > environment.yes, here you are creating the class in the global environment, which is not locked (!).> I have attached an example of the kind of function below, and I am > using R 2.5.0. > > Where should I look for information on undertanding what might be > going on here? Why does this way of working succeeds with setMethod > but not setClass?I think that setMethod works because the methods package has already created an environment within the appropriate name space (try 'ls(<pkg name space>, all=TRUE)' and look at the '.__<etc>' bindings). What setMethod does is modfies this environment, rather than the environment of the name space. Hope that helps, and is not too misleading. Martin> The intention here is to provide an easy way for creating extended > classes that will inherit from a class already defined, instead of > array as in the example. > > Many thanks, > > > Iago > > > defineClass <- function(name, dimnames) { > # dim, names[2], dimnames[2] > if(names(dimnames)[1] != 'iter') > dimnames <- c(list(iter=1), dimnames) > dim <- unlist(lapply(dimnames, length)) > > # validity > foo <- "validity <- function(object) {" > # foo <- c(foo, "browser()") > foo <- c(foo, paste("if(all(names(dimnames(object)) != c('", > paste(names(dimnames), collapse="','", sep=""), "')))", > sep="")) > foo <- c(foo, "stop(paste('dimnames are not correct for class', > name))") > foo <- c(foo, "return(TRUE)}") > eval(parse(text=foo)) > > # setClass > setClass(name, representation('array'), > prototype=prototype(array(as.numeric(NA), dim=dim, > dimnames=dimnames), units='NA'), > validity=validity, where=sys.parent(n=-2)) > > # setValidity > setValidity(name, validity) > > # constructors > eval(parse(text=paste("setGeneric('", name, > "', function(object, ...) standardGeneric('", name, "'))", > sep=""))) > > setMethod(name, signature(object='ANY'), > function(object, ...) > return(FLPar(object, ..., class=name)) > ) > setMethod(name, signature(object='missing'), > function(...) { > return(FLPar(..., class=name)) > } > ) > > } > > --- Iago Mosqueira Systems Modelling Cefas Pakefield Rd. Lowestoft > NR33 0HT U.K. Tel +44 (0) 1502 558003 > > > *********************************************************************************** > This email and any attachments are intended for the named\...{{dropped}}
Hi Martin, Thanks very much, your suggestion on the "where" argument did the trick. Many thanks, Iago>> "Iago Mosqueira (Cefas)" <Iago.Mosqueira at cefas.co.uk> writes:>> > Hi Martin,> >> > Thanks very much for your help.> >> >> Hi Iago --> >>> >> Here's my attempt at an answer.> >>> >> "Iago Mosqueira (Cefas)" <Iago.Mosqueira at cefas.co.uk> writes:> >>> >> > set of S4 classes. But setClass is choking when my function> >> is called> >> > isnide a package, telling about an error in> exists(cname, where). I> >> > assume this to be a problem with the environment where> the class is> >> > created. If I define my creator function in a package> and load it,> >> > when I call it I get> >> >> >> > Error in assign(mname, def, where): cannot add bindings> to a locked> >> > environment> >>> >> You're trying to create a class in the package name space, and the> >> name space is locked (cannot be modified) after the package is> >> loaded. If your intention is programmatically generate> static classes> >> (in a kind of macro-like way) then I think you could still use this> >> approach but ensure that the classes are created when the> package is> >> loaded (e.g., by including a class creation function that> is actually> >> evaluated, not just defined, in your package code).> >> > Yes, I have tried that. Inmediatly after the creator function is> > defined I use it to create a new class. And this class is exported> > in the package NAMESPACE. Still, it says the package environment is> > locked, although the call to setClass is part of the package.>> Try removing the 'where' argument in setClass; or replacing it with> topenv(parent.frame()) -- the top environment when the package is> loaded is the name space.>> Also, for fun,>> >> > # validity> >> > foo <- "validity <- function(object) {"> >> > # foo <- c(foo, "browser()")> >> > foo <- c(foo, paste("if(all(names(dimnames(object)) != c('",> >> > paste(names(dimnames), collapse="','", sep=""), "')))",> >> > sep=""))> >> > foo <- c(foo, "stop(paste('dimnames are not correct for class',> >> > name))")> >> > foo <- c(foo, "return(TRUE)}")> >> > eval(parse(text=foo))>> you can write something like>> validity <- function (object) {> msg <- NULL> cat("checking", name, "/", class(object), "validity\n")> if (all(names(dimnames(object)) != c("iter", "y")))> msg <- paste("dimnames are not correct for class", name)> if (is.null(msg)) TRUE> else msg> }>> and continue to include validity=validity in setClass. This will use> R's lexical scope to figure out the correct name, etc for each class> created. Note also that the idea is that the validity function either> returns TRUE or a character string, not itself calling 'stop'.>> >> > # constructors> >> > eval(parse(text=paste("setGeneric('", name,> >> > "', function(object, ...) standardGeneric('",> >> name, "'))",> >> > sep="")))>> I had expected that>> setGeneric(name,> function(object, ...) standardGeneric(name))>> would work, but apparently not. This might be a bit cleaner way to> create complcated substitutions;>> eval(substitute({> setGeneric(NAME,> function(object, ...) {> standardGeneric(NAME)> })> }, list(NAME=name)))>> >> > setMethod(name, signature(object='missing'),> >> > function(...) {> >> > return(FLPar(..., class=name))> >> > }> >> > )>> It's a bit surprising that this setMethod works, as the function> signature is different from the generic. This might cause problems> further down the line. I would have thought that the correct> formulation would be along the lines of>> setMethod(name, signature(object='missing'),> function(object, ...) {> FLPar(..., class=name)> })>> Martin> --> Martin Morgan> Bioconductor / Computational Biology> http://bioconductor.org>