Hi all, I would like to access the name of a variable passed to an S4 method. For a function, I would do this: f <- function(x) as.character(substitute(x)) This also works for a the examples I have tried for methods that do not have extra, non-dispatch args: setGeneric("A", function(x, ...) standardGeneric("A")) setMethod("A", signature(x="character"), function(x) as.character(substitute(x))) However, I'm seeing strange behavior if the method uses an extra argument: setMethod("A", signature(x="numeric"), function(x, y) as.character(substitute(x))) num <- 1 A(num) [1] "x" A(num, 2) [1] "x" Is there a way to make this work? I came up with one workaround that uses a non-standard generic (see below). It seems that when a method uses extra args matching '...' in the generic, an extra frame is used in the evaluation and so substitute() isn't reaching the same place as without extra args. Thanks in advance for pointers to doc or suggestions. + seth ## here is a non-standard generic that gives me the behavior I want setGeneric("B", function(x, ...) { x.name <- as.character(substitute(x)) standardGeneric("B") }) setMethod("B", signature(x="character"), function(x, y) { penv <- sys.frames() penv <- penv[[length(penv)-2]] get("x.name", envir=penv) }) Observation: Without an extra arg in the method, the appropriate environment would be penv[[length(penv) - 1]], but the presence of the extra arg results in an extra environment in the evaluation, hence we need -2.
Try defining your method like this. I don't know how generally this works but it seems to work here. setMethod("A", signature(x="numeric"), function(x, y) as.character(substitute(x, sys.frame(-1)))) On 1/25/06, Seth Falcon <sfalcon at fhcrc.org> wrote:> Hi all, > > I would like to access the name of a variable passed to an S4 method. > For a function, I would do this: > > f <- function(x) as.character(substitute(x)) > > This also works for a the examples I have tried for methods that do > not have extra, non-dispatch args: > > setGeneric("A", function(x, ...) standardGeneric("A")) > > setMethod("A", signature(x="character"), > function(x) as.character(substitute(x))) > > However, I'm seeing strange behavior if the method uses an extra > argument: > > setMethod("A", signature(x="numeric"), > function(x, y) as.character(substitute(x))) > > num <- 1 > > A(num) > [1] "x" > > A(num, 2) > [1] "x" > > Is there a way to make this work? I came up with one workaround that > uses a non-standard generic (see below). > > It seems that when a method uses extra args matching '...' in the > generic, an extra frame is used in the evaluation and so substitute() > isn't reaching the same place as without extra args. > > Thanks in advance for pointers to doc or suggestions. > > > + seth > > ## here is a non-standard generic that gives me the behavior I want > > setGeneric("B", function(x, ...) { > x.name <- as.character(substitute(x)) > standardGeneric("B") > }) > > setMethod("B", signature(x="character"), > function(x, y) { > penv <- sys.frames() > penv <- penv[[length(penv)-2]] > get("x.name", envir=penv) > }) > > Observation: Without an extra arg in the method, the appropriate > environment would be penv[[length(penv) - 1]], but the presence of the > extra arg results in an extra environment in the evaluation, hence we > need -2. > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel >
On Wed, 25 Jan 2006, Seth Falcon wrote:> I would like to access the name of a variable passed to an S4 method. > For a function, I would do this: > > f <- function(x) as.character(substitute(x)) > > This also works for a the examples I have tried for methods that do > not have extra, non-dispatch args: > > setGeneric("A", function(x, ...) standardGeneric("A")) > > setMethod("A", signature(x="character"), > function(x) as.character(substitute(x))) > > However, I'm seeing strange behavior if the method uses an extra > argument: > > setMethod("A", signature(x="numeric"), > function(x, y) as.character(substitute(x))) > > num <- 1 > > A(num) > [1] "x" > > A(num, 2) > [1] "x" > > Is there a way to make this work? I came up with one workaround that > uses a non-standard generic (see below). > > It seems that when a method uses extra args matching '...' in the > generic, an extra frame is used in the evaluation and so substitute() > isn't reaching the same place as without extra args.The reason you get an extra frame is that when the method's argument doesn't match the generic's, setMethod makes a function with the generic's argument list that calls your method (renamed ".local") and makes that new function the real method. E.g., > setMethod("A",sig=signature(x="character"), function(x,n){ if(nchar(x)>n) stop("nchar(x)>n") deparse(substitute(x)) }) [1] "A" > getMethod("A",sig=signature(x="character")) Method Definition: function (x, ...) { .local <- function (x, n) { if (nchar(x) > n) stop("nchar(x)>n") deparse(substitute(x)) } .local(x, ...) } ... This has 2 bothersome side effects. One is yours: > A(paste("One","Two"), 10) [1] "x" and the other is that the function mentioned in the error report is misleading: > A("xyz", 1) Error in .local(x, ...) : nchar(x)>n You can workaround both problems by making a method that looks somewhat like the the one generated by setMethod but gets some details right for your function. E.g., > setMethod("A",sig=signature(x="character"), function(x, ...) { A.character <- function(x,n,x.name){ if(nchar(x)>n) stop("nchar(x)>n") x.name } x.name <- deparse(substitute(x)) A.character(x, ..., x.name=x.name) } ) This gives a suggestive function name in the error message > A(paste("One","Two"), 3) Error in A.character(x, ..., x.name = x.name) : nchar(x)>n and lets you use substitute: > A(paste("One","Two"), 10) [1] "paste(\"One\", \"Two\")" Thus you don't have to guess how many frames or environments lie between your method and the generic. This works in R and Splus. ---------------------------------------------------------------------------- Bill Dunlap Insightful Corporation bill at insightful dot com 360-428-8146 "All statements in this message represent the opinions of the author and do not necessarily reflect Insightful Corporation policy or position."