Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Force-evaluate function argument in order to "trick" substitute()?

say I have a function from an R package which I want to wrap in a closure for convenience and better code readability. For simplicity, let’s assume the function looks as follows:

fun <- function(text) {
    as.character(substitute(text))
}

When I call that function from the console, e.g.

fun(text = "bar")

the returned value is "bar", which in my case is the desired behavior. The reason the function is written the way it is, is that in case I call

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

fun(text = bar)

the output is also "bar". Just for context. I obviously didn’t write this function by myself, I just want to use it.

The problem: When I call fun from within a function, like e.g.

fun2 <- function(foo) {
    fun(text = foo)
}
fun2(foo = "bar")

the output will always be "foo" instead of "bar", regardless of what I assign to foo in the call to fun2. Ofc I know this is how substitute() is intended to work, but this makes it impossible (or at least very nasty?), to programmatically work with the function fun.

My Question: Is there a way to achieve the desired behavior without rewriting fun?

Thanks a lot in advance 🙂

>Solution :

You could construct the call inside fun2 and evaluate it:

fun2 <- function(foo) {
  eval(call("fun", foo))
}

fun2(foo = "bar")
#> [1] "bar"

The problem here is that it now no longer works if you pass bar unquoted:

fun2(foo = bar)
#> Error in eval(call("fun", foo)) : object 'bar' not found

So you would need something like:

fun2 <- function(foo) {
  if(exists(as.character(substitute(foo)), parent.frame())) {
    eval(call("fun", foo))
  } else {
    eval(call("fun", as.character(substitute(foo))))
  }
}

Which now works either way:

fun2(foo = "bar")
#> [1] "bar"

fun2(foo = bar)
#> [1] "bar"

The problem here is that if bar actually exists, then it will be interpreted as the value of bar, so we have:

fun2(foo = "bar")
#> [1] "bar"

fun2(foo = bar)
#> [1] "bar"

bar <- 1

fun2(foo = bar)
#> [1] "1"

Which is presumably not what you intended.

However, if you were going to do it this way, it probably no longer makes sense to call fun at all. Perhaps the easiest thing to do is

fun2 <- function(foo) {
  as.character(match.call()$foo)
}

fun2("bar")
#> [1] "bar"

fun2(bar)
#> [1] "bar"

bar <- 1

fun2(bar)
#> [1] "bar"
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading