Understanding function substitute()
The scope of this article is to clarify, at least for me, the behaviour of function substitute()
.
As a confusing example consider:
x <- 0
substitute( x )
x
and
f <- function() {
x <- 0
substitute(x)
}
f()
[1] 0
As you can see function substitute()
returns different result when called either from within the global environment or within a function environment
Assuming some prior knowledge of environment
and promise
objects you can analyse the documentation for substitute()
.
Documentation says:
Substitution takes place by examining each component of the parse tree as follows:
In practice, function substitutes()
takes to arguments:
R
expressionThe return value of the function depends on the interaction between the two arguments.
We will take into account five cases corresponding to four types of objects to pass to argument expr
For each object we will examine the interaction with the env
argument
When symbol x
does not exist within env
Symbol x
is returned
substitute(expr = x , env = list())
x
When x
is a constant either in env
or globalenv
the constant is returned.
When x
is a variable in env
and env
is not the global environment, the value of the variable is returned.
substitute(x, env = list ( x = 0 ))
[1] 0
When x
is a variable globalenv
, the symbol x
is returned
x <- 0
substitute(x, env = globalenv())
x
When x
is a promise in env
, the the expression slot of the promise is returned.
env <- new.env()
delayedAssign('x', 0, assign.env = env)
substitute(x, env = env)
[1] 0
Note that argument assign.env
of function delayedAssign()
strictly require an environment.
When the expression is a function formal, therefore a promise, the same rule applies:
f <- function(x) {
substitute(x)
}
f(x = 0)
[1] 0
When x
is a promise in globalenv
, the symbol slot of the promise is returned. Basically, as soon as the promise object is accessed the variable is evaluated and then passed to substitute
delayedAssign('x', 0, assign.env = globalenv())
substitute(x, env = globalenv())
x
When exp is an R
expression within env
substitute returns the expression
substitute(e, env = list( e = quote(x+1)))
x + 1
When exp is an R
expression within globalenv
substitute returns the symbol associated to the expression
e <- quote(x+1)
substitute(expr = e, env = globalenv())
e
When function substitute()
is used outside globalenv
, it really substitutes
f <- function(){
a <- 1
b <- 2
substitute(a+b+c)
}
f()
1 + 2 + c
When function substitute()
is used inside globalenv
, no substitution occurs and the names associated to either the variable, the promise or the expression is returned unchanged. In practice, function substitute()
within globalenv()
works like function quote()
a <- 1
b <- 2
substitute(a+b+c)
a + b + c
Because of its behaviour, developing and experimenting with function substiute()
may be difficult.
In order to overcome this problem, Hadley in adv-r proposes function subs()
in package pryr
:
pryr::subs
function (x, env = parent.frame())
{
if (identical(env, globalenv())) {
env <- as.list(env)
}
substitute_q(substitute(x), env)
}
<bytecode: 0x562053d5dd10>
<environment: namespace:pryr>
Function subs()
has the same behavior either inside or outside globalenv
:
Within globalenv:
x <- 1
pryr::subs(x+1)
1 + 1
Outside globalenv:
f <- function(x) {
pryr::subs(x+1)
}
f(1)
1 + 1
The inner part of function subs()
relies on function substitute_q
.
A less formal, but more esplicative implementation of substitute_q()
is
substitute_q <- function (x, env) {
call <- substitute(substitute(y, env), list(y = x))
eval(call)
}
When function substitute_q()
is called fron globalenv
, it returns the evaluated expression
as it does when called from any other environment
f <- function(x) {
a <- 2
substitute_q(x, env = environment())
}
f(quote(a+1))
2 + 1
If you see mistakes or want to suggest changes, please create an issue on the source repository.
For attribution, please cite this work as
Spano (2021, Dec. 29). andreaspano blog: Substitute. Retrieved from https://andreaspano.github.io/posts/2021-12-29-substitute/
BibTeX citation
@misc{spano2021substitute, author = {Spano, Andrea}, title = {andreaspano blog: Substitute}, url = {https://andreaspano.github.io/posts/2021-12-29-substitute/}, year = {2021} }