-
-
Save JonasMoss/43a80eca0ee32bc04ec62b7e3e4bfa0c to your computer and use it in GitHub Desktop.
A function that evaluates a call as if it was defined in a specified environment.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#' Evaluates a call as if its function was defined in a specified environment | |
#' | |
#' When a name is encountered in the definition of a function, the search path | |
#' for that name is given by the defining environment of the function. This is | |
#' good behaviour, since it allows simple reasoning about how a function should | |
#' behave: If two calls to a function defined in a constant environment \code{e} | |
#' yield different results, this must be because they are given different | |
#' arguments. | |
#' | |
#' Sometimes, a function is defined to make messy code more readable, but is | |
#' only intended to be used once. In these cases, you might want to call the | |
#' function as if it was defined in the body of the work you're currently | |
#' working with. That is what this function does for you. | |
#' | |
#' @param call The function call to evaluate. | |
#' @param env The environment where the function is defined. Defaults to | |
#' \code{parent.frame}, which makes the defining environment equal to the | |
#' current environment. | |
#' @param quote Logical; If \code{TRUE}, substitutes the argument. | |
#' @return The value of the function call when evaluated in the specified | |
#' environment. | |
#' @examples | |
#' x = 3 | |
#' f = function() x^2 | |
#' g = function() { | |
#' x = 10 | |
#' S(f()) | |
#' } | |
#' g() # Evalutes to 100 | |
#' f() # Evalutes to 9 | |
#' S(f()) # Evaluates 9 | |
S = function(call, env = parent.frame(), quote = TRUE) { | |
call = if(quote) substitute(call) else call | |
fn = call[[1]] # Name of the function as a 'name' object. | |
args = as.list(call[-1]) # A list of unevaluated arguments. | |
# I make a temporary environment where a copy of the function is stored. I | |
# do this because we don't want the name of the function to crash with any of | |
# the names used in this current function definition. | |
# | |
# After we have done this, we will assign the correct defining environment to | |
# fn, namely 'env'. | |
temporary_environment = new.env() | |
temporary_environment[[deparse(fn)]] = getFunction(deparse(fn)) | |
environment(temporary_environment[[deparse(fn)]]) = env | |
# 'as.call' creates a call out of the function name and the list of arguments. | |
# The call is then evaluated inside the environment 'temporary_environment', | |
# where the new (copy) of the function f is stored. When f is called, it | |
# believes that it has been defined inside 'env', so everything works as it | |
# should. | |
eval(expr = as.call(c(fn, args)), envir = temporary_environment) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment