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

Gabor Grothendieck ggrothendieck at gmail.com
Fri Aug 21 14:12:45 CEST 2009


I think you need to consider particular scenarios in which this is used
and the characteristics of the user since there are going to be tradeoffs
in the design.

It may be that by examining typical usage cases that you find that
the fun.global2 situation (i.e. fun.global calls fun.global2 calls setVar)
is unlikely, that passing an object or function to fun.global is not
that onerous for the user or that simply using parent.frame()$setVar(4)
in fun.global is not seen as a significant problem for them either.

On Fri, Aug 21, 2009 at 7:57 AM, Renaud Gaujoux<renaud at cbio.uct.ac.za> wrote:
> And to make things a bit more difficult: I also use the multicore package.
> So I guess that naively modifying setVar in my package namespace would not
> be appropriate as each process needs to define its own setVar function.
> Unless setVar is implemented so that it knows which process is calling it
> and respond accordingly.
> I need to give more thinking to that than what I expected to.
> Do you think I'm just bothering myself in vain (:() or that one can get to a
> sound and robust solution (except the object one)?
>
> Gabor Grothendieck wrote:
>>
>> Certainly its possible to dynamically write to the package's namespace.
>> For example, lattice does that. It stores various lattice options there.
>>
>> Injecting setVar is not really more conservative though.   Consider that
>> when you modify the environment of a function in main:
>>
>> environment(fun.global) <- environment()
>>
>> you are actually modifying a copy of fun.global and the original
>> fun.global
>> is not changed at all so you are not disturbing any external objects
>> whereas
>> injecting setVar and setting it back again actually modifies the
>> environment
>> external to main.
>>
>> On Fri, Aug 21, 2009 at 7:20 AM, Renaud Gaujoux<renaud at cbio.uct.ac.za>
>> wrote:
>>
>>>
>>> Ah! It looks interesting and seems more conservative than changing the
>>> actual environment. I could do the restore within an on.exit to deal with
>>> the error eventuality.
>>> Do you think could define a dummy setVar function in my package
>>> namespace,
>>> and change its behaviour on runtime?
>>>
>>> This way, as soon as he has loaded the package, the user will be able to
>>> call setVar anywhere in his functions. What setVar does would be set by
>>> my
>>> main code that runs before calling the user's function.
>>>
>>> Gabor Grothendieck wrote:
>>>
>>>>
>>>> Another possibility is that you could inject setVar into
>>>> fun.global's environment.
>>>>
>>>> First save any existing setVar in fun.global's environment to tmp
>>>> and then assign our own setVar there.  We then run fun.global()
>>>> and reverse the changes we made in fun.global's environment
>>>> setting everything back to what it was before.
>>>>
>>>> If setVar is called from fun.global2 instead of fun.global then
>>>> its up to the user to ensure that fun.global2 can access
>>>> fun.global's environment.
>>>>
>>>> fun.global <- function() { message('fun.global'); fun.global2() }
>>>> fun.global2 <- function() { message("fun.global2"); setVar(2) }
>>>>
>>>> main <- function() {
>>>>  l.var <- 0
>>>>  e <- environment(fun.global)
>>>>
>>>>  tmp <- if (exists("setVar", e)) get("setVar", e) ##
>>>>  assign("setVar", function(value) { message("setVar"); l.var <<- value
>>>> },
>>>> e)
>>>>  fun.global()
>>>>  if (is.null(tmp)) rm(setVar, envir = e) else assign("setVar", tmp, e)
>>>>
>>>>  print(l.var)
>>>> }
>>>> main()
>>>>
>>>>
>>>> Yet another possibility is to simply mandate that the user call setVar
>>>> directly from fun.global and ask them to do it like this:
>>>>
>>>> fun.global <- function() parent.frame()$setVar(4)
>>>> main <- function() {
>>>>      l.var <- 0
>>>>      setVar <- function(value) { message("set Var"); l.var <<- value }
>>>>      environment(fun.global) <- environment()
>>>>      fun.global()
>>>>      print(l.var)
>>>> }
>>>> main()
>>>>
>>>>
>>>>
>>>>
>>>> On Fri, Aug 21, 2009 at 6:33 AM, Renaud
>>>> Gaujoux<renaud at mancala.cbio.uct.ac.za> 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 ?
>>>>> -- 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.
>>>>>>>
>>>>>>>
>>>>>
>>>>> ______________________________________________
>>>>> 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