Maiasaura
Maiasaura

Reputation: 32996

Why is my function looking for an input with the exact same name instead of working with the variable I passed?

test is a function to check if an object exists in the global environment, is not empty, and belongs to a particular class.

test <- function(foo, response=TRUE) {
    if (missing(foo)) {
        response <- FALSE
    }
    if (response) {
        if (!exists(as.character(substitute(foo)), envir = .GlobalEnv)) {
            response <- FALSE
        }
    }
    if (response) {
        response <- ifelse(class(foo) != "numeric", FALSE, TRUE)
    }
return(response)
}

Now in foobar and a dozen other functions, I want to make sure foo is the right kind of object I want before proceeding with anything else.

foobar <- function(foo)
{
    if(test(foo)) {
    cat ("Yes, I have foo! \n")
    }
    if(!test(foo)) {
        cat("Sorry, not a valid foo")
    }
}

 >ls()
[1] "foobar" "test"
 >test(a)
[1] FALSE
 >a <- "foobar"
 >test(a)
[1] FALSE
 >a <- 1
 >test(a)
[1] TRUE
 >foobar(a)
Sorry, not a valid foo
 >
# what the???
 >ls()
[1] "a"      "foobar" "test"
 >foo <- 1
 >foobar(foo)
Yes, I have foo!
 >

Upvotes: 1

Views: 124

Answers (2)

IRTFM
IRTFM

Reputation: 263451

Objects loose their original names when handed off more than once. The copies get assigned new localnames. You need to grab the name on the first pass and then test with ls()

 foobar <- function(foo)
 { fooname <- deparse(substitute(foo)); print(fooname)
     if(test(fooname) ) {
     cat ("Yes, I have foo! \n")
     }
     if(!test(fooname) ) {
         cat("Sorry, not a valid foo")
     }
 }

 test <- function(foo, response=TRUE) { 
     if (missing(foo)) {
         response <- FALSE
                        }
     if (response) {
         if ( foo %in% ls( envir = .GlobalEnv) ) {
             response <- TRUE }else {response <- FALSE}
                                                }

 return(response)
                                      }
 foobar(after)
# [1] "after"
#Yes, I have foo! 

Upvotes: 3

mathematical.coffee
mathematical.coffee

Reputation: 56935

To verify, the problem is in the fact that the substitute is nested.

When R looks at foobar(a), it runs test(foo) within the foobar function, and so the variable that the test function looks at is called foo.

I'll start with a toy example to make things easier to explain. The library function, like your test function, interprets its argument via the variable name. i.e. library(MASS) loads the 'MASS' library, not the string that is contained inside the variable 'MASS'.

Now I'll make a function f that just calls library - this mirrors your foobar function:

f <- function(x) {
    library(x)
}

Now let's try:

> f(MASS)
Error in library(x) : there is no package called ‘x’

Oh no! It didn't work! How come? Because remember, within the library code, it substitutes the variable passed in. i.e. library <- function(lib,...) substitute(lib).

So f(MASS) goes to function(x) library(x), and hence it's like I typed library(x) straight into the command line -- library is only trying to load x, and not x's value, MASS.

OK, we can fix this: we just need to change library(x) to library(substitute(x)), since substitute(x) is MASS and we'll then end up with library(MASS), right?

f <- function(x) {
    library(substitute(x))
}

Let's try:

> f(MASS)
Error in library(substitute(x)) : 'package' must be of length 1

Urgh, what happened? Within f, the substitute(x) is not being evaluated, because library purposefully doesn't evaluate the expression it gets fed, because then typing library(MASS) in the command line wouldn't work.

So we really want to save substitute(x) as a variable and then perform library on that variable.

The only problem is that even if we do y <- substitute(x); library(y) within f, we always run into this problem that the argument fed into library is never evaluated. So doing this will cause the same as the first error: 'there is no package called y'.

How can we fix this? We need to somehow indirectly call library with substitute(x) as the argument, where substitute(x) is evaluated.

Aha! We can use do.call! (note: I didn't come up with this on my own, I was guided by this post to the R mailing list on nested substitutes:

f <- function(x) {
    do.call(library,list(substitute(x)))
}

This does exactly what we want - it calls library but passes substitute(x) in as the library. It evaluates substitute(x) first, since we haven't directly written library(substitute(x)). nifty right?

> f(MASS) # no error!
# see if MASS is loaded - check if function 'lda' is there:
> exists('lda',mode='function') 
[1] TRUE # huzzah!

Solution for your case

So, applying this lesson to your question, try:

foobar <- function(foo)
{
    if ( do.call(test,list(substitute(foo))) ) # see the do.call & substitute?
        cat ("Yes, I have foo! \n")
    else
        cat("Sorry, not a valid foo")
}

Let's see:

> ls()
[1] "foobar" "test"  
> test(a)
[1] FALSE
> a <- 'foobar'
> test(a)
[1] FALSE
> a <- 1
> test(a)
[1] TRUE
> foobar(a)
Yes, I have foo! 

Huzzah! (by the way: thanks for asking this question, because the answer is something I've always wanted to know).

Upvotes: 2

Related Questions