Ken Benoit
Ken Benoit

Reputation: 14902

How to avoid promise already under evaluation warning for setting default argument as a function of another argument

I've read the other answers for issues related to the "promise already under evaluation" warning, but I am unable to see how they can help me avoid this problem.

Here I have a function that for one method, takes a default argument value that is a function of another value.

myfun <- function(x, ones = NULL) {
    UseMethod("myfun")
}


myfun.list <- function(x, ones = NA) {
    data.frame(x = x[[1]], ones)
}

ones <- function(x) {
    rep(1, length(x))
}

So far, so good:

myfun(list(letters[1:5]))
##   x ones
## 1 a   NA
## 2 b   NA
## 3 c   NA
## 4 d   NA
## 5 e   NA

But when I define another method that sets the default for the ones argument as the function ones(x), I get an error:

myfun.character <- function(x, ones = ones(x)) {
    myfun(as.list(x), ones)
}

myfun(letters[1:5])
## Error in data.frame(x = x[[1]], ones) : 
##  promise already under evaluation: recursive default argument reference or earlier problems? 

For various reasons, I need to keep the argument name the same as the function name (for ones). How can I force evaluation of the argument within my fun.character? I also need this to work (which it does):

myfun(letters[1:5], 1:5)
##   x ones
## 1 a    1
## 2 a    2
## 3 a    3
## 4 a    4
## 5 a    5

Thanks!

Upvotes: 1

Views: 1984

Answers (1)

LyzandeR
LyzandeR

Reputation: 37879

One would need to look deep into R's (notorious) environments to understand exactly, where it tries to find ones. The problem is located in the way supplied and default arguments are evaluated within a function. You can see this link from the R manual and also an explanation here.

The easy solution is to tell R where to look for it. It will save you the hassle. In your case that's the global environment.

Changing method myfun.character to tell it to look for ones in the global environment:

myfun.character <- function(x, ones = get('ones', envir = globalenv())(x)) {

  myfun(as.list(x), ones)

}

will be enough here.

Out:

myfun(letters[1:5])
#  x ones
#1 a    1
#2 a    1
#3 a    1
#4 a    1
#5 a    1

myfun(letters[1:5], 1:5)
#  x ones
#1 a    1
#2 a    2
#3 a    3
#4 a    4
#5 a    5

Upvotes: 2

Related Questions