Search code examples
roopparametersparameter-passingpass-by-reference

How to get the name of the current environment THIS within a function


It looks like you can have one function pass data back transparently to a parent function with the assign operation.

Example (Setup)


calling = function()
    {
    a = 25;
    cat("\n ...CALLING... a = ", a, "\n");
    assign("a", a, envir=parent.frame(1) );  # ?sys.calls
    }


caller = function()
    {
    a = 15;
    cat("\n ...CALLER... a = ", a, "\n");
    
    pf = parent.frame(); ## how to say THIS current FRAME
    cat("\n ...CALLER_FRAME... pf = \n");
    print(pf);
    
    cat("\n\n\n");
    
    calling();
    cat("\n ...CALLER*... a = ", a, "\n");
    }

RUN

a = 5;
cat("\n ...GLOBAL... a = ", a, "\n");
caller();
# scope, should mess with GLOBAL = 5
cat("\n ...GLOBAL*... a = ", a, "\n");

OUTPUT

> a = 5;
> cat("\n ...GLOBAL... a = ", a, "\n");

 ...GLOBAL... a =  5 
> caller();

 ...CALLER... a =  15 

 ...CALLER_FRAME... pf = 
<environment: R_GlobalEnv>




 ...CALLING... a =  25 

 ...CALLER*... a =  25 
> # scope, should mess with GLOBAL = 5
> cat("\n ...GLOBAL*... a = ", a, "\n");

 ...GLOBAL*... a =  5 

Question

So above, the GLOBAL a was not altered, but calling was able to update the value of a for caller without returning the value. That is, calling passed back the value to caller with assign.

The question is in the code, in caller, how do I get THIS current frame reference?

How could I use that if I had more complicated nesting? Pass envir to calling that may have been a few functions deep? This reference Can you pass-by-reference in R? has some discussion, but I don't fully under THIS current frame reference of caller?


Solution

  • As @zephryl said, environment() returns the currently active environment. You could write your calling() function like this:

    calling <- function(envir = parent.frame()) {
      a <- 25
      assign("a", a, envir = envir)
    }
    

    and then it would by default assign in the caller, but if you specified the envir argument it could assign there instead.

    Then you could do something like this:

    myenv <- new.env()
    calling(envir = myenv)
    myenv$a
    #> [1] 25
    

    to do the assignment in a custom environment. And if you wrote caller() like this:

    caller <- function() {
      a <- 2
      calling(envir = environment())
      cat("in caller, a=", a, "\n")
    }
    a <- 1
    caller()
    #> in caller, a= 25
    print(a)
    #> [1] 1
    

    it would work just like

    caller2 <- function() {
      a <- 3
      calling()
      cat("in caller2, a=", a, "\n")
    }
    
    a <- 4
    caller2()
    #> in caller2, a= 25
    print(a)
    #> [1] 4
    

    because while evaluating caller(), environment() gives a reference to the active environment, and while evaluating calling(), parent.frame() points to the environment of the caller, which would be the same thing.