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<ren...@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@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@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@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.

Reply via email to