Heisenberg Heisenberg - 2 months ago 15
R Question

Non standard evaluation in Hadley's advanced R book

In Hadley's Advanced R book, there is a piece of code that I cannot understand the output.

f <- function(x) substitute(x)
g <- function(x) deparse(f(x))
g(x + y ^ 2 / z + exp(a * sin(b)))

Why do they all return
? Especially when

g <- function(x) deparse(substitute(x))

returns the
, and
"x + y ^ 2 / z + exp(a * sin(b))"
as expected.


First, some background information: A promise is an unevaluated argument. A promises comprises of two parts: 1) the code / expression that gives rise to this delayed computation (this code can be viewed by substitute or pryr::promise_info), and 2) the environment where this code / expression is created and should be evaluated in (this environment can be viewed by pryr::promise_info).

The question is also clearer if you changed the g() function to

g <- function(x) deparse(f(whatever))

you would always get "whatever". This is because when g() calls f(whatever), it passes a promise object to f()--this object has the code whatever and the environment of g()'s execution environment. Then, the substitute within f() looks at this promise object and returns the code / expression of that promise, which is whatever in this case.

The code and the environment of the promise object can be confirmed by running the following code:

library(pryr) # need to install it
f <- function(x) {

g <- function(x) {

The bottom line is you'll get back whatever you pass to f(whatever). That's why it's not a good idea to separate these functions. One work around would be to use

g <- function(...) deparse(f(...))

This way the parameter is passed through to f() and not renamed in g().

On the other hand, g <- function(x) deparse(substitute(x)); g(1:10) produces 1:10 because, in this case, substitute is looking at promise object x (in contrasts to the promise object whatever in the above case). Promise x here has the code 1:10 and the environment R_GlobalEnv. (Again, this can be checked using g <- function(x) { print(promise_info(x) ; deparse(substitute(x)). So substitute(x) returns 1:10 as expected.