[R] Scope and assignment: baffling

William Dunlap wdunlap at tibco.com
Thu Apr 1 19:00:22 CEST 2010


> -----Original Message-----
> From: r-help-bounces at r-project.org 
> [mailto:r-help-bounces at r-project.org] On Behalf Of Jeff Brown
> Sent: Wednesday, March 31, 2010 6:45 PM
> To: r-help at r-project.org
> Subject: [R] Scope and assignment: baffling
> 
> 
> Hi,
> 
> The code below creates a value, x$a, which depending on how 
> you access it
> evaluates to its initial value, or to what it's been changed 
> to.  The last
> two lines should, I would have thought, evaluate to the same 
> value, but they
> don't.
> 
> f <- function () {
> 	x <- NULL;

It would be better to say
      x <- list()
instead of x<-NULL, since the following x$a<-
coerces it to a list.

> 	x$a <- 0;
> 	x$get.a <- function () {
> 		x$a;
> 	};
> 	x$increment.a <- function () {
> 		x$a <<- x$a + 5;
> 	};
> 	x
> };
> x <- f();

Let's make things a tad clearer by naming the output
of f() 'globalX':
   globalX <- f()
globalX is a list containing a [virtual] copy of f's x, as it
stood when f() returned it.  You cannot modify globalX
without doing something like globalX$something<-xxx.
f's x remains in the environment created when you ran
f().  Each time you run f() you create an new environment
(with a new version of f's x).  A function's evaluation
environment generally goes away when the function is done,
but it remains if there are any references to the environment
from f()'s return value.  If the return value contains
functions defined in f() then they refer to f()'s evaluation
environment, so it hangs around.  When you run
    globalX$increment.a()
you are modifying f()'s environment's x$a, not globalX$a.

Usually such function are written so the state variables
are not in the output structure, as in:
    f <- function() {
        fA <- 0
        retval <- list()
        retval$get.a <- function() { fA }
        retval$increment.a <- function() { fA <<- fA + 5 }
        retval
    }
Then you can use it as you did but it is clear that
retval itself doesn't contain any state.  You can find
the possible state variables with
    > globalX <- f()
    > globalX$increment.a()
    > objects(environment(globalX$get.a))
    [1] "fA"     "retval"
    > eval(quote(fA), environment(globalX$get.a))
    [1] 5
    > globalX$increment.a()
    > eval(quote(fA), environment(globalX$get.a))
    [1] 10
    > globalX$get.a()
    [1] 10
The output of objects() suggests you might want
to clean up the code by not naming 'retval', just
returning list(get.a=function()fA, ...).  Then
your environment won't carry around an unused
copy of retval.
    
Bill Dunlap
Spotfire, TIBCO Software
wdunlap tibco.com 

> x$increment.a();
> x$get.a();
> x$a;
> 
> This can be repeated; each time you call x$increment.a(), the value
> displayed by x$get.a() changes, but x$a continues to be zero.
> 
> Is that totally weird, or what?
> 
> Thanks,
> Jeff
> -- 
> View this message in context: 
> http://n4.nabble.com/Scope-and-assignment-baffling-tp1747582p1
747582.html
> Sent from the R help mailing list archive at Nabble.com.
> 
> ______________________________________________
> R-help at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide 
> http://www.R-project.org/posting-guide.html
> and provide commented, minimal, self-contained, reproducible code.
> 



More information about the R-help mailing list