[R] eval and evironments: call local function in a global function

Duncan Murdoch murdoch at stats.uwo.ca
Fri Aug 21 14:36:12 CEST 2009

On 8/21/2009 6:33 AM, Renaud Gaujoux wrote:
> Thanks Duncan, I agree that touching the environments is risky and not 
> robust. I rather go for another solution.
> But the function solution still require to pass an extra object to the 
> user's function. I'd like the user to simply be able to call function 
> setVar as if it was a standard R function (visible from any of his -- 
> possibly nested -- functions), but this function would act on a variable 
> local to the main call, that I can setup on runtime. This variable 
> should be protected from direct access as much as possible (my idea with 
> the local layer was something to implement kind of a private variable). 
> Maybe it's just impossible ?

If you want the function visible to users, it should be an exported 
function from your NAMESPACE.  But then it is hard to have variables 
local to each caller.

Another approach that doesn't mess with things that aren't yours is to 
ask the user to provide some local storage, and pass that to setVar each 
time.   A sloppier way to do the same thing is to tell the user that 
they need to set up a variable with a fixed name, and setVar will in its 
caller's frame for that variable.  (The second solution is getting close 
to the edge of touching things that aren't yours...)

If the local storage is set up as an environment then the user will see 
your changes to it, because environments are passed by reference.  (You 
could also use an external pointer if you wanted it to be more opaque, 
but that's more work for you.)

The bottom line is that there are lots of ways to do what you want, but 
each has tradeoffs.  If you are intending your code to work in a complex 
environment, keeping it simple, and working within a very restrictive 
set of rules will really help.

Duncan Murdoch

> -- Sorry to insist :) --
> Renaud
> Duncan Murdoch wrote:
>> On 8/20/2009 4:27 AM, Renaud Gaujoux wrote:
>>> Hi,
>>> in my project I want the user to be able to write hook functions that 
>>> are in turn called in my main code. I'd like the user's hooks to be 
>>> able to call some function that set a variable outside their running 
>>> environment.
>> The trick is that this variable is not global, but defined
>>> on runtime before calling the hooks, and I don't want to leave any 
>>> trace (i.e. global variables) after the main code has finished.
>> The best way to do this is to pass the function (setVar below) as an 
>> argument to the user's function.  If it's the user's function, you 
>> have no business messing with it by changing its environment.  How do 
>> you know the user didn't spend hours working out some crazy scheme of 
>> creating a nested function with a carefully crafted environment, and 
>> evaluation of his function depends on you leaving it alone?
>>> I thought that the following would work but it doesn't. I guess I got 
>>> too messy with environment and enclosures:
>>> # global function defined by the user
>>> fun.global <- function(){
>>>     message('fun.global')
>>>     setVar(5) #
>>> }
>> Pass setVar as an arg:
>> fun.global <- function(setVar) {
>>     message('fun.global')
>>     setVar(5)
>> }
>>> # my main code
>>> main <- function(){
>>>     message('main')
>>>     # define a function to set some local variable
>>>     setVar <- local({
>>>     l.var <- 0
>>>     function(value){
>>>         message('setVar')
>>>        l.var <<- value
>>>     }
>>>     })
>>>     .workspace <- environment(setVar)
>>>     environment(setVar) <- new.env()
>>>     eval(fun.global(), enclos=environment(setVar))
>>>     print(get('l.var', envir=.workspace, inherits=FALSE))
>>> }
>> I wouldn't bother with the extra layer of local(), just put l.var in 
>> main's evalution frame.  (Since you're the one writing setVar, you can 
>> avoid any name clashes.)  That is:
>> main <- function() {
>>     message('main')
>>     l.var <- 0
>>     setVar <- function(value) {
>>     message('setVar')
>>     l.var <<- value
>>     }
>>     fun.global(setVar)
>>     print(l.var)
>> }   
>> Duncan Murdoch
>>> main()
>>> I get the following output:
>>>  > main
>>>  > fun.global
>>>  > Error in fun.global() : could not find function "setVar"
>>> There is definitely a problem of lookup somewhere. I first tried 
>>> without eval, as I thought that function setVar would then be defined 
>>> in a parent environment of the call to fun.global, but it does not 
>>> work either.
>>> Can anybody tell me what's the problem and how I should do my stuff?
>>> Thanks,
>>> Renaud
>>> ______________________________________________
>>> 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