2

Have a look to this "simple" functions :

test <- function(x,...){
    UseMethod("test",x)
}

test.default<-function(x,y,data){
  message("default")
  print(deparse(substitute(x)))
  print(deparse(substitute(y)))
  print(deparse(substitute(data)))
  print(match.call())
}

test.formula <- function(x,...){
  message("formula")
  print(deparse(substitute(x)))
  print(match.call())
}

Everything is fine

data(iris)
test.formula(Sepal.Length~Petal.Width,iris)
test.default(Sepal.Length,Petal.Width,iris)
test(Sepal.Length~Petal.Width,iris)

Except this one :

test(Sepal.Length,Petal.Width,iris)

Because of NSE : object 'Sepal.Length' not found

Any idea ?

Vincent Guyader
  • 2,927
  • 1
  • 26
  • 43
  • I'm not getting an error and the third "test" returns the same as the first one. I wouldn't expect R to be able to find a column name unless your function supplied the `iris` object as an environment. – IRTFM Mar 18 '17 at 22:40
  • @42- do you try this one : `test(Sepal.Length,Petal.Width,iris)` ? – Vincent Guyader Mar 18 '17 at 22:42
  • Yes but `Sepal.Length` is not an R name on the search path. It's an attribute of `iris` with a character value. – IRTFM Mar 18 '17 at 22:45
  • @42- the error is not with `test(Sepal.Length~Petal.Width,iris)` but with `test(Sepal.Length,Petal.Width,iris)`, I know why I have an error (because of NSE), I just want to find a "nice" way to use NSE with UseMethod. – Vincent Guyader Mar 18 '17 at 22:49

3 Answers3

1

It is not easy not come up with a solution because you have not specified your end game here, but I think I agree with 42- that S3 dispatch might not be the tool. You might be looking for lazyeval::lazy_dots

library("lazyeval")
tezt <- function(data, ... ){
  dots <- lazyeval::lazy_dots(...)
  dots
}

You give your ... to lazy_dots and then you can deal with it.

str( tezt(iris, Sepal.Length, Petal.Width) )
#> List of 2
#>  $ :List of 2
#>   ..$ expr: symbol Sepal.Length
#>   ..$ env :<environment: 0x7fedb11bb720> 
#>   ..- attr(*, "class")= chr "lazy"
#>  $ :List of 2
#>   ..$ expr: symbol Petal.Width
#>   ..$ env :<environment: 0x7fedb11bb720> 
#>   ..- attr(*, "class")= chr "lazy"
#>  - attr(*, "class")= chr "lazy_dots"

or:

str( tezt(iris, Sepal.Length ~ Petal.Width) )
#> List of 1
#>  $ :List of 2
#>   ..$ expr: language Sepal.Length ~ Petal.Width
#>   ..$ env :<environment: 0x7fedb11bb720> 
#>   ..- attr(*, "class")= chr "lazy"
#>  - attr(*, "class")= chr "lazy_dots"

Also, you might be interested in hadley/rlang

Romain Francois
  • 17,432
  • 3
  • 51
  • 77
0

I found a solution, but a tricky one...

tryCatch.W.E <- function(expr)
   {
         W <- NULL
         w.handler <- function(w){ # warning handler
        W <<- w
        invokeRestart("muffleWarning")
           }
         list(value = withCallingHandlers(tryCatch(expr, error = function(e) e),
                                                                 warning = w.handler),
                     warning = W)
     }

test <- function(x, ...) {

  if (inherits(tryCatch.W.E(x)$value,"error")) { 

    return(test.default(x,...))
    }

  UseMethod("test", x)
}

test.default <- function(x, y, data) {
  message("default")
  print(deparse(substitute(x)))
  print(deparse(substitute(y)))
  print(deparse(substitute(data)))
  print(match.call())
}

test.formula <- function(x, ...) {
  message("formula")
  print(deparse(substitute(x)))
  print(match.call())
}
test.formula(Sepal.Length ~ Petal.Width, iris)
test.default(Sepal.Length, Petal.Width, iris)
test(Sepal.Length ~ Petal.Width, iris)

Now this is ok :

test(Sepal.Length, Petal.Width, iris)
Vincent Guyader
  • 2,927
  • 1
  • 26
  • 43
0

I'd be more satisfied with:

require(ggplot2)

test <- aes
test(Sepal.Length, Petal.Width, iris)
#    
* x -> Sepal.Length
* y -> Petal.Width
*   -> iris

The problem with using S3 dispatch, is that there is no value and therefore no class for a "naked" Sepal.Length. The aes function does not use S3 dispatch, but rather goes immediately to match.call()[-1].

IRTFM
  • 258,963
  • 21
  • 364
  • 487