Skip to content

Substitute / delayedAssign (was: Substitute unaware when promise objects are evaluated)

6 messages · McGehee, Robert, Peter Meilstrup, Duncan Murdoch +1 more

#
Duncan, Thank you for the clarification on how delayedAssign works. Should R-level interfaces to promise objects ever become available, I expect they would at time come in handy.

On the subject of substitute and delayedAssign, I do have a follow-up question for the list. I'm trying to convert a named list of expression objects into an environment of promise objects. After conversion, each expression in the list will be automatically evaluated when the variable with the same name is accessed in the environment. Effectively, I'm trying to create a hash table of promise objects.

Here's the code I wrote that works just fine.

x <- list(a=3, b=expression(a+2), sleep=expression(Sys.sleep(2)))
env <- new.env()
for (i in seq(x)) {
	key <- names(x)[i]
	.Internal(delayedAssign(key,
				   eval(substitute(x[[i]], list(x=x, i=i)))[[1]],
				   eval.env=env, assign.env=env))
}	
env$b     # 3+2
[1] 5
env$sleep # Sleeps for 2 seconds
NULL  

The "problem" is that R CMD check complains that I shouldn't be using .Internal() to access the delayedAssign function. However, if I don't use .Internal(), then delayedAssign puts another substitute around my call that prevents the 'i' iterator variable from being evaluated at the correct time, which causes all variables to get the value x[[i]] for the very last value of 'i'.

Can I safely ignore this R CMD check warning about .Internal, or is there a better way to write this code?

Thanks, Robert



-----Original Message-----
From: Duncan Murdoch [mailto:murdoch.duncan at gmail.com] 
Sent: Wednesday, May 15, 2013 6:04 PM
To: McGehee, Robert
Cc: R-Devel (r-devel at r-project.org)
Subject: Re: [Rd] Substitute unaware when promise objects are evaluated
On 13-05-15 11:54 AM, McGehee, Robert wrote:
I think you misunderstand promises.

A promise has two (or three, depending how you count) parts:  an 
expression with an associated environment, and a value.  The value isn't 
filled in until the expression is evaluated, but the expression doesn't 
go away then.  You can still see it until you change the variable that 
holds the promise.
The documentation for substitute may not be clear on this, but for a 
promise, the env argument will be ignored.  It was the eval.env argument 
to delayedAssign that set the promise's environment.
Not at R level. In C code you could, but you probably shouldn't.  Think 
of promises as values where you can look up the expression that gave the 
value, and sometimes delay the calculation until you need it.

Duncan Murdoch
#
On 16/05/2013 9:06 AM, McGehee, Robert wrote:
You should never call .Internal.  Arguments to internal functions may 
change without notice.

Here's one way to write your example without it.

x <- list(a=3, b=expression(a+2), sleep=expression(Sys.sleep(2)))
env <- new.env()

mydelay <- function(i) {
   expr <- x[[i]]
   name <- names(x)[i]
   do.call(delayedAssign, list(x=name, value=substitute(eval(expr), 
list(expr=expr)),
           eval.env=env, assign.env=env))
}

for (i in seq(x)) mydelay(i)

Duncan Murdoch
#
On 13-05-16 11:17 PM, Peter Meilstrup wrote:
I like that solution, except for one thing:  I don't see an easy way to 
control the environment where those expressions will be executed.  Since 
you've set them as defaults on the arguments, they will be evaluated in 
the evaluation frame of f(), and that might not be what we want. An 
obvious example of the problem would be

e <- makePromiseEnv(alist(a = ls()))

I don't know what Robert would want

e$a

to print, but one somewhat natural version would be to have it evaluate 
the ls() in the environment from which makePromiseEnv was called, i.e. 
the global environment in this case.  Neither your solution nor mine do 
this, but I can see how to modify mine, since it makes the evaluation 
environment of the expression explicit.  Can you see a modification that 
would do that with your approach?

Duncan Murdoch
#
On May 16, 2013, at 15:06 , McGehee, Robert wrote:

            
These things are slippery, but the usual way out is to figure out exactly which expression you want to call, compute the expression unevaluated, and evaulate it.

Something like

e <- bquote(delayedAssign( .(names(x)[i]), .(x[[i]]), eval.env=env, assign.env=env))
print(e)
eval(e)

(of course remove the print(e) once you're sure that it is doing the right thing)