Reputation: 47
I want to pass the content of an object that is defined outside a function to the function's body. My problem might be easier to understand with an example:
a <- 'hello world'
foo <- function() print(a)
rm(a)
foo()
Currently, this obviously throws an error, because a
is not defined anymore when calling foo()
.
My desired outcome is that I want foo
to look like function() print('hello world')
Upvotes: 2
Views: 82
Reputation: 269441
1) local Capture a
using a local
like this. This fixes the value of a
to what it was when foo
was defined. Note that the a
on the right hand side of a <- a
is the a
already defined whereas the a
on the left hand side is a new object, also called a
, that is defined within the local
. No packages are used.
a <- 'hello world'
foo <- local({
a <- a
function() print(a)
})
rm(a)
foo()
## [1] "hello world"
2) substitute Another alternative is to substitute the value of a
into the body of foo
like this. This fixes a
in foo
to the value that it was when the substitute
is run. No packages are used.
foo <- function() print(a)
a <- 'hello world'
body(foo) <- do.call("substitute", list(body(foo), list(a = a)))
rm(a)
foo()
## [1] "hello world"
3) function which generates foo Another possibility is to create a function which generates foo. Note that R uses lazy evaluation so we need to use force
to ensure that the value of a
at the time that foogen
runs is used; otherwise, it will delay the evaluation until after a
is removed causing an error. This fixes a
in foo
to the value that is passed to foogen
. No packages are used.
foogen <- function(a) {
force(a)
function() print(a)
}
a <- 'hello world'
foo <- foogen(a)
rm(a)
foo()
## [1] "hello world"
4) proto The proto package can be used to create an object p
containing both foo
and a
.
library(proto)
p <- proto(foo = function(.) print(a))
a <- 'hello world'
p$a <- a
rm(a)
p$foo()
## [1] "hello world"
Upvotes: 4
Reputation: 173793
You do not need to use eval(parse(...))
here, or an external package. Using eval
and parse
is generally not a good idea for a few different reasons, including the amount of fun that a malicious user could have exploiting it. This is becoming increasingly relevant as R is used more and more in web apps that accept user input.
There are a few ways to do this in base R without eval(parse(...))
or any external dependencies. One approach is to have a function factory:
a <- 'hello world'
foo_maker <- function(x) as.function(list(x = a, quote(print(x))))
So now we can do:
foo <- foo_maker(a)
rm(a)
foo()
#> [1] "hello world"
Upvotes: 1
Reputation: 47
Just found out how to solve the problem using the whisker
package and eval(parse(...))
:
library(whisker)
a <- 'hello world'
foo <- eval(parse(text = whisker.render('function() print("{{b}}")',
data = list(b = a))))
foo()
rm(a)
foo()
Upvotes: 0