[Rd] A doubt about substitute() after delayedAssign()

Duncan Murdoch murdoch.duncan at gmail.com
Tue May 1 22:32:18 CEST 2012


On 12-05-01 4:21 PM, Philippe Grosjean wrote:
> On 29/04/12 13:50, Duncan Murdoch wrote:
>> On 12-04-29 3:30 AM, Philippe Grosjean wrote:
>>   >  Hello,
>>   >
>>   >  ?delayedAssign presents substitute() as a way to look at the expression
>>   >  in the promise. However,
>>   >
>>   >  msg<- "old"
>>   >  delayedAssign("x", msg)
>>   >  msg<- "new!"
>>   >  x #- new!
>>   >  substitute(x) #- x (was 'msg' ?)
>>   >
>>   >  Here, we just got 'x'... shouldn't we got 'msg'?
>>   >
>>   >  Same result when the promise is not evaluated yet:
>>   >
>>   >  delayedAssign("x", msg)
>>   >  substitute(x)
>>   >
>>   >  In a function, that works:
>>   >
>>   >  foo<- function (x = msg) substitute(x)
>>   >  foo()
>>   >
>>   >  Did I misunderstood something? It seems to me that substitute() does not
>>   >  behaves as documented for promises created using delayedAssign().
>>
>> I don't think this is well documented, but substitute() doesn't act the
>> same when its "env" argument is the global environment. So this works
>> the way you'd expect:
>>
>> e<- new.env()
>> msg<- "old"
>> delayedAssign("x", msg, assign=e)
>> msg<- "new"
>> e$x
>> substitute(x, e)
>>
>> I forget what the motivation was for special-casing globalenv().
>>
>> Duncan Murdoch
>
> In the corresponding C code, there is a comment telling that it is for
> "historical reasons". Are these historical reasons that important that
> there is no way using R code (not C code) to know if a symbol is bind to
> a promise in .GlobalEnv?

I don't know.  I believe I lost an argument similar to yours a few years 
ago, so I won't spend time on this again.

Duncan Murdoch

Anyway, I have filled a bug report because, at
> least the documentation of ?delayedAssign and ?substitute should be
> clarified, as well as, the example for delayedAssign... But, unless for
> a good reason, it would be better to perform substitution, even in
> .GlobalEnv, or alternatively, to provide a function like promiseExpr()
> to get it.
>
> Here are a couple of potentially useful functions (using the inline
> package for convenience, and also note that I had to use a trick of
> passing the substituted name of the variable to get the promise at the C
> level... which would be unnecessary if these would be special base
> functions that pass unevaluated arguments):
>
> ## is.promise(): check if a name is bind to a promise
> require(inline)
> code<- '
>     SEXP obj;
>     if (!isString(name) || length(name) != 1)
>       error("name is not a single string");
>     if (!isEnvironment(envir))
>       error("envir should be an environment");
>     obj = findVar(install(CHAR(STRING_ELT(name, 0))), envir);
>     return ScalarLogical(TYPEOF(obj) == PROMSXP);
> '
> is.promise<- cfunction(signature(name = "character", envir =
> "environment"),
> 	code)
> formals(is.promise)<- alist(x =, name = deparse(substitute(x)),
> 	envir = parent.frame(1))
>
> ## isEvaluated(), determine if a promise has already been evaluated
> ## return always TRUE is the name is bind to something else
> ## than a promise
> code<- '
>     SEXP obj;
>     if (!isString(name) || length(name) != 1)
>       error("name is not a single string");
>     if (!isEnvironment(envir))
>       error("envir should be an environment");
>     obj = findVar(install(CHAR(STRING_ELT(name, 0))), envir);
>     if (TYPEOF(obj) == PROMSXP&&  PRVALUE(obj) == R_UnboundValue) {
> 	return ScalarLogical(FALSE);
>     } else {
> 	/* if it is not a promise, it is always evaluated! */
> 	return ScalarLogical(TRUE);
>     }
> '	
> isEvaluated<- cfunction(signature(name = "character", envir =
> "environment"),
> 	code)
> formals(isEvaluated)<- alist(x =, name = deparse(substitute(x)),
> 	envir = parent.frame(1))
> 	
> ## promiseExpr() retrieve the expression associated with a promise...
> ## even if it is in .GlobalEnv, what subsitute() does not!
> code<- '
>     SEXP obj;
>     if (!isString(name) || length(name) != 1)
>       error("name is not a single string");
>     if (!isEnvironment(envir))
>       error("envir should be an environment");
>     obj = findVar(install(CHAR(STRING_ELT(name, 0))), envir);
>     if (TYPEOF(obj) == PROMSXP) {
> 	return PREXPR(obj);
>     } else {
> 	return R_NilValue;
>     }
> '	
> promiseExpr<- cfunction(signature(name = "character", envir =
> "environment"),
> 	code)
> formals(promiseExpr)<- alist(x =, name = deparse(substitute(x)),
> 	envir = parent.frame(1))
>
> ## promiseEnv() get the evaluation environment associated with a promise
> code<- '
>     SEXP obj;
>     if (!isString(name) || length(name) != 1)
>       error("name is not a single string");
>     if (!isEnvironment(envir))
>       error("envir should be an environment");
>     obj = findVar(install(CHAR(STRING_ELT(name, 0))), envir);
>     if (TYPEOF(obj) == PROMSXP) {
> 	return PRENV(obj);
>     } else {
> 	return R_NilValue;
>     }
> '	
> promiseEnv<- cfunction(signature(name = "character", envir =
> "environment"),
> 	code)
> formals(promiseEnv)<- alist(x =, name = deparse(substitute(x)),
> 	envir = parent.frame(1))
> 	
> ## reeval() reavaluate a promise that has been already evaluated,
> ## An environment for the evaluation is required since PRENV is set
> ## to NULL on promise evaluation
> code<- '
>     SEXP obj;
>     if (!isString(name) || length(name) != 1)
>       error("name is not a single string");
>     if (!isEnvironment(envir))
>       error("envir should be an environment");
>     if (!isEnvironment(evalenv))
>       error("evalenv should be an environment");
>     obj = findVar(install(CHAR(STRING_ELT(name, 0))), envir);
>     if (TYPEOF(obj) == PROMSXP) {
> 	/* TODO: should we use the same precautions as in forcePromise(), line
> 297 of eval.c? */
> 	/* TODO: what to do here, if not evaluated yet?*/
> 	SEXP val;
> 	val = eval(PRCODE(obj), evalenv);
> 	SET_PRVALUE(obj, val);
> 	return PRVALUE(obj);
>     } else {
> 	return R_NilValue;
>     }
> '	
> reeval<- cfunction(signature(name = "character", envir = "environment",
> 	evalenv = "environment"), code)
> formals(reeval)<- alist(x =, name = deparse(substitute(x)),
> 	envir = parent.frame(1), evalenv = parent.frame(1))
> rm(code)
>
> msg<- "old"
> delayedAssign("x", msg)
> y<- msg
> is.promise(x) # TRUE
> isEvaluated(x) # FALSE, promise not evaluated yet!
> is.promise(y) # FALSE
> isEvaluated(y) # TRUE (always when not a promise)
> msg<- "new"
> x
> y
> is.promise(x) # Still TRUE
> isEvaluated(x) # Now TRUE, the promise is evaluated
> promiseExpr(x) # Also work in .GlobalEnv, on the contrary to
> substitute()! For "historical" reasons!
> promiseExpr(y) # NULL because it is not a promise
> promiseEnv(x) # It becomes NULL once the promise is evaluated!
> msg<- "brand new message..."
> x
> reeval(x)
> x
>
> Best,
>
> Philippe Grosjean
>
>
>
>
>



More information about the R-devel mailing list