Will Hipson
Will Hipson

Reputation: 364

Changing a package function's environment in R

I have a situation where I have a function whose environment I want to change. This function is in a local custom R package.

Here's my setup. I have an R package project called testpkg with one function in /R called do_stuff.R:

#' A function that clearly does stuff
#' @export
do_stuff <- function() {
  Sys.sleep(1)
  "Done"
}

In an interactive script I'm trying to assign this function to another environment. I want to use the assign function so I can do this programmatically. Here's what I've been trying:

devtools::load_all()
env <- new.env()
assign("do_stuff", do_stuff, env)

# See if it worked (nope, environment is namespace:testpkg)
> env$do_stuff
# function() {
#   Sys.sleep(1)
#   "Done"
# }
# <environment: namespace:testpkg>

If I use environment()<- it does seem to work:

devtools::load_all()
environment(do_stuff) <- new.env()

# See if it worked (yes, environment is different)
> do_stuff
#function() {
#  Sys.sleep(1)
#  "Done"
# }
# <environment: 0x7f9d1c3a8fd8>

But this isn't ideal since I need this to work programmatically. I realize this seems like a pretty strange thing to do. Essentially, I need to make my custom functions transportable to work with the future package for async, but that's beside the point.

Upvotes: 2

Views: 540

Answers (1)

MrFlick
MrFlick

Reputation: 206197

In R, functions are lexically scoped. This means that they look up free variables in the environment where they are defined. In order for that to work, functions need to keep track of that environment. This is the same same for both anonymous and named functions. You can retrieve/set this environment using the envionment() and environment<-() functions. This is just the like class() and names() functions. They alter properties of the object itself.

When you use assign(), you are just pointing a particular symbol/name to an object. Different environments may assign different values to the same name. This has nothing to do with the environment property attached to a function when it was created. A named function in one environment can easily point to a different environment to resolve free variable names.

Here's a demonstration of the way things work

other <- new.env()
other$x <- 15
x <- 2
# The global environment and the `other` enviroment
# now have two different values for `x`

foo <- function(num) x+num
# Note that "x" is a free variable in the function
# by default the environment() of foo will be the
# global environment since that was where it was first
# defined.

foo(5)
# [1] 7
other$foo(5)
# Error: attempt to apply non-function
# The function doesn't exist yet in other

assign("foo", foo, envir=other)
other$foo(5)
# [1] 7
# even though we assigned the name "foo" to point
# to the "foo" function, it still looks for "x" in 
# the global environment

environment(foo) <- other
foo(5)
# [1] 20
# Note how changing the environment property of the function 
# changed where it looked up the value for `x`. We now use
# the `x` value from `other`

other$foo(5)
# [1] 7
# but the name "foo" in the other environment still points to 
# the original version of "foo" that has the environment set 
# to the current global environment

Upvotes: 1

Related Questions