Using substitute from inside an S4 method

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Using substitute from inside an S4 method

Seth Falcon-2
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.

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Using substitute from inside an S4 method

Gabor Grothendieck
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 <[hidden email]> 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.
>
> ______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
>

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Using substitute from inside an S4 method

Bill Dunlap
In reply to this post by Seth Falcon-2
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."

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel