Skip to content

Finding the environment of a promise

8 messages · Henrique Dallazuanna, Hadley Wickham, Simon Urbanek +2 more

#
Hi all,

Is it possible to determine the environment in which a promise will be
evaluated?  e.g.

f <- function(code) { force(code) }
f({
  a <- 1
  b <- 2
})

Is there any way to tell from within f that a and b will be created in
the global environment?

Thanks,

Hadley
#
On 9/9/2009 9:30 AM, Hadley Wickham wrote:
I don't think so in R code, but C code to do it would be possible.  It 
needs to be in C code to avoid forcing the promise.

I think we'd be reluctant to make an R function available to do this, 
because it requires non-standard evaluation, and it's not something a 
normal function should care about.  (The promise belongs to the caller, 
not to the function:  why would the function need to play with it?  It 
should be happy with the value, or maybe a text representation of the 
promise, for labelling purposes.)

Duncan Murdoch
#
Thanks Duncan - I thought that might be the case.
True, but there are plenty of abnormal functions in R that demonstrate
the usefulness (and danger!) of non-standard evaluation.
Is there any way to get a text representation apart from with
deparse(substitute(x))?  I think I remember asking you about this some
time ago, but it is still the case that blocks/promises don't store
srcref information?  This come up recently while trying to make a
function that would both run a block or code and save it to a separate
file (a sort of poor man's sweave when you just want to include a
small portion of your code in a report).  With blocks, the best I
could come up with was:

named_block <- function(name, code) {
  text <- deparse(substitute(code), width = 100)
  # evaluate code
  force(code)

  # construct valid filename
  filename <- gsub("[^a-z0-9]+", "-", tolower(name))
  filename <- gsub("^-|-$", "", filename)
  filename <- paste(filename, ".r", sep = "")

  out <- text[!grepl("\\{|\\}", text)]
  out <- gsub("^    ", "", out)

  writeLines(out, filename)
}


b <- 1
named_block("test", {
  # This is a comment
  a <- 1
})

print(a)

The problem of course is that this loses comments.

You can do better with functions, but it requires more typing and
messing around with environments to ensure everything is scoped
correctly:

named_block <- function(name, f) {
  text <- attr(f, "source")

  code <- text[!grepl("^function \\(\\)\\{$|\\}", text)]
  code <- gsub("^  ", "", code)

  # evaluate code
  eval(parse(text = code), env = parent.frame())

  # construct valid filename
  filename <- gsub("[^a-z0-9]+", "-", tolower(name))
  filename <- gsub("^-|-$", "", filename)
  filename <- paste(filename, ".r", sep = "")

  writeLines(code, filename)
}


b <- 1
named_block("test", function (){
  # This is a comment
  a <- 2
})


# Make sure objects created in correct environment
print(a)
#
On Sep 9, 2009, at 9:40 , Henrique Dallazuanna wrote:

            
^-- this will always return R_GlobalEnv (see ?sys.frame - which = 0 by  
default) regardless of the function and promise.

Also the question was about the environment of the promise, not the  
function. Technically a promise can be evaluated anywhere since it  
ignores the evaluation environment and will use its creation  
environment which is what Hadley was trying to get at (and as Duncan  
was saying it's not something that is or should be available at R  
level as it's an internal implementation detail).

Cheers,
Simon
#
On 9/9/2009 10:53 AM, Hadley Wickham wrote:
Promises don't store srcrefs, but the evaluator does (in R-devel), a 
statement at a time.  So you might be able to look back through the call 
stack to find where the call came from.  (I forget whether any of this 
is accessible from within R.  Look at the implementation of traceback() 
and browser(), which both access this information.)

Duncan Murdoch
1 day later
#
One of the (many) things on my TODO list is to add some sort of
reflection mechanism for examinging the status of bindings --
standard, active, delayed but evaluated, delayed but not yet
evaluated, etc.  The interface might have a flavor like

     bindingStatus(name, envir) returns one of "standard", "delayed",
     "delayed-evaluated"

or something along these lines.  It is important not to think in terms
of promises, which happen to exist as internal implementation devices
at the moment but might not in the future.

For cases like delayed but not yet evaluated it may make sense to also
provide access to things like the expression or code (which need not
be the same), or the environment. But there are some issues.  For
example, once a delayed assignment is evaluated the environment is no
longer needed, and dropping it povides opportunities for memory
reclamation (the interpreter does this now by setting the environment
field of a promise to NULL once the promise is evaluated).  We
wouldn't want a reflection mechanism to make tha impossible. Also, in
cases like a call f(1) there is no good reason for creating a delayed
evaluation, since the result of evaluating the argument is clear.  The
interpreter doesn't do such an optimization at this time but it might
be useful in the future, and again we would not want a reflection
mechanism to get in the way of this.

So it's worth thinking about, especially if a compelling use case is
available, but there are subtleties and it would not be a good idea to
move too hastily.

Best,

luke
On Wed, 9 Sep 2009, Duncan Murdoch wrote: