Is it possible to add an on.exit expr to the parent call? If so, how?
For example, say that parentOnExit(expr) is a function implementing this. Then for the following code:
f <- function() {
parentOnExit(print("B"))
print("A")
}
I want to see "A" printed, then "B".
Background: What brought this to mind was the following... we have a collection of functions, some of which call others, which require a resource that should be shared from the topmost call down and which also should be closed upon exiting the topmost function. Eg, a connection to a remote server which is expensive to open. One pattern for this is:
foo <- function(r=NULL) {
if (is.null(r)) { # If we weren't passed open connection, open one
r <- openR()
on.exit(close(r))
}
bar(r=r) # Pass the open connection down
}
I was hoping to abstract those three lines down to:
r <- openIfNull(r) # Magically call on.exit(close(r)) in scope of caller
Now that I think about it though, perhaps it's worth some repeated code to avoid anything too magical. But still I'm curious about the answer to my original question. Thank you!
I have seen in this recent mail discussion (https://stat.ethz.ch/pipermail/r-devel/2013-November/067874.html) that you can use do.call for this:
f <- function() { do.call("on.exit", list(quote(cat('ONEXIT!\n'))), envir = parent.frame()); 42 }
g <- function() { x <- f(); cat('Not yet!\n'); x }
g()
#Not yet!
#ONEXIT!
#[1] 42
Using this feature and an additional ugly trick to pass the R connection object to the caller environment, it seems to solve the problem:
openR <- function(id = "connection1") {
message('openR():', id)
list(id)
}
closeR <- function(r) {
message('closeR():', r[[1]])
}
openRIfNull <- function(r) {
if (length(r)) return(r)
# create the connection
r <- openR("openRIfNull")
# save it in the parent call environment
parent_env <- parent.frame()
assign("..openRIfNull_r_connection..", r, envir = parent_env)
do.call("on.exit", list(quote(closeR(..openRIfNull_r_connection..))), envir = parent_env)
r
}
foo <- function(r = NULL) {
message('entered foo()')
r <- openRIfNull(r)
bar(r = r) # Pass the open connection down
message('exited foo()')
}
bar <- function(r) {
message('bar()')
}
example use:
foo()
# entered foo()
# openR():openRIfNull
# bar()
# exited foo()
# closeR():openRIfNull
foo(openR('before'))
# entered foo()
# openR():before
# bar()
# exited foo()
I was intrigued by the problem and tried a couple of ways to solve it. Unfortunately, they didn't work. I'm therefore inclined to believe that it can't be done. ...But someone else might be able to prove me wrong!
Anyway, I though I'd post my failed attempts so that they are recorded. I made them so that they would print "ONEXIT!" after "Not yet!" if they worked...
1 - First, simply try to evaluate the on.exit in the parent environment:
f <- function() { eval(on.exit(cat('ONEXIT!\n')), parent.frame()); 42 }
g <- function() { x<-f(); cat('Not yet!\n'); x }
g() # Nope, doesn't work!
This doesn't work, probably because the on.exit function adds stuff to the current stack frame, not the current environment.
2 - Step up the game and try to return an expression that is evaluated by the caller:
f <- function() { quote( {on.exit(cat('ONEXIT!\n')); 42}) }
g <- function() { x<-eval(f()); cat('Not yet!\n'); x }
g() # Nope, doesn't work!
This doesn't work either, probably because eval has its own stack frame, different from g.
3 - Bring my A-game, and try to rely on lazy evaluation:
h <- function(x) sys.frame(sys.nframe())
f <- function() { h({cat('Registering\n');on.exit(cat("ONEXIT!\n"));42}) }
g <- function() { x<-f()$x; cat('Not yet!\n'); x }
g() # Worse, "ONEXIT!" is never printed...
This one returns an environment to the caller, and when the caller accesses "x" in it, the expression including on.exit is evaluated. ...But it seems on.exit does not register at all in this case.
4 - Hmm. There is one way that might still work: a .Call to some C code that calls on.exit. It might be that calling C won't add another stack frame... This is a bit too complex for me to test now, but maybe some RAPI/RCpp guru could give it a shot?
I remain confused, but if Tommy can't do it, I suspect I won't be able to either. This does the first task and since it seemed so simple I thought I must be missing something:
f <- function() {
on.exit(print("B"))
print("A")
}
Second effort:
txtB <- textConnection("test b")
txt <-textConnection("test A")
f <- function(con) { df <- read.table(con);
if( isOpen(txtB)){ print("B open")
eval( close(txtB), env=.GlobalEnv ) }
return(df) }
txtB #just to make sure it's still open
# description class mode text
# "\"test b\"" "textConnection" "r" "text"
# opened can read can write
# "opened" "yes" "no"
dat <- f(txt); dat
#[1] "B open"
# V1 V2
#1 test A
txtB
#Error in summary.connection(x) : invalid connection
(OK, I edited it to close a connection within the calling environment.)
So what am I missing? (It wasn't clear to me as I tested this that connections actually have environments.)
Though this question is quite old, there is a simple fix for any future visitors:
Use add=TRUE (I don't find the documentation very clear.)
f <- function() {
on.exit(expr = print("B"),
add = TRUE)
print("A")
}
A another solution is using withr::defer() which has more options and better documentation.
The vignette is especially helpful.
Related
I want to write a function in R which grabs the name of a variable from the context of its caller's caller. I think the problem I have is best understood by asking how to compose deparse and substitute. You can see that a naive composition does not work:
# a compose operator
> `%c%` = function(x,y)function(...)x(y(...))
# a naive attempt to combine deparse and substitute
> desub = deparse %c% substitute
> f=function(foo) { message(desub(foo)) }
> f(log)
foo
# this is how it is supposed to work
> g=function(foo) { message(deparse(substitute(foo))) }
> g(log)
log
I also tried a couple of variations involving eval.parent but with no luck. Any help is appreciated.
Clarification: I'm not looking for a synonym for deparse(substitute(...)), e.g. match.call()[[2]] - what I'm looking for is a way to define a function
desub = function(foo) {
...
# What goes here?
}
such that the definition of f above produces the same answer as g. It should look like this:
> f=function(foo) { message(desub(foo)) }
> f(log)
log
Perhaps match.call could be of use in the body of desub above, but I'd like to know how. Thanks!
As you surmised, this is an issue with environments. The reason why the function f does not give log when you call f(log), is that the environment in which substitute is called, namely the evaluation environment of desub, does not contain a binding to log.
The remedy is to evaluate the call to substitute in the proper environment, and modify desub accordingly:
desub <- function(x, env = parent.frame()) {
deparse(eval(substitute(substitute(x)), envir = env))
}
Now f does what it was intended to do:
f(log)
#> log
Thanks to #egnha and #akrun for the brave attempts. After playing around a bit I found a solution that works.
This fragment:
desub <- function(y) {
e1=substitute(y)
e2=do.call(substitute,list(e1), env=parent.frame())
deparse(e2)
}
gives:
> f <- function(x) message(desub(x))
> f(log)
log
Update:
With help from Mark Bravington on the R-devel list, I was able to generalize this to multiple frames. I thought I should post it here, because it's a bit more useful than the above, and because there was a tricky workaround involving (possibly buggy?) behavior in parent.frame().
# desub(v,0)=="v"
# desub(v,1)==deparse(substitute(v))
# desub(v,2)==name of v in grandparent's frame
# etc.
desub = function(y,n=1) {
env=environment();
for(i in 0:n) {
y = do.call(substitute, list(substitute(y)), env=env)
env = do.call(my_mvb_parent, list(), env=env)
}
deparse(y)
}
# helper:
#
# - using mvb.parent.frame fixes problems with capture.output and
# weird cycling behavior in the built-in parent.frame
#
# - this wrapper makes mvb.parent.frame not throw an error when we get
# to globalenv()
my_mvb_parent=function() {
library(mvbutils)
tryCatch(
mvb.parent.frame(2),
error=function(e) { globalenv()})
}
if(1) {
# example code
g2=function(t) {
for(i in 0:5) {
res=desub(t,i);
print(res);
res1=capture.output(desub(t,i))
stopifnot(capture.output(res)==res1)
}
}
g1=function(z) g2(z)
g=function(y) g1(y)
g(log)
# prints:
## [1] "t"
## [1] "z"
## [1] "y"
## [1] "log"
## [1] "log"
## [1] "log"
}
I have below function. I cannot alter the function in any way except the first block of code in the function.
In this simple example I want to display apply some function on returning object.
The point is the name of variable returned by function may vary and I'm not able to guess it.
Obviously I also cannot wrap the f function into { x <- f(); myfun(x); x }.
The below .Last.value in my on.exit call represents the value to be returned by f function.
f <- function(param){
# the only code I know - start
on.exit(if("character" %in% class(.Last.value)) message(print(.Last.value)) else message(class(.Last.value)))
# the only code I know - end
# real processing of f()
a <- "aaa"
"somethiiiing"
if(param==1L) return(a)
b <- 5L
"somethiiiing"
if(param==2L) return(b)
"somethiiiing"
return(32)
}
f(1L)
# function
# [1] "aaa"
f(2L)
# aaa
# [1] 5
f(3L)
# integer
# [1] 32
Above code with .Last.value seems to be working with lag (so in fact not working) and also the .Last.value is probably not the way to go as I want to use the value few times like if(fun0(x)) fun1(x) else fun2(x), and because returned value might be a big object, copy it on the side is also bad approach.
Any way to use on.exit or any other function which can help me to run my function on the f function results without knowing result variable name?
In a similar way to how you are modifying the function, you could easily wrap it as well. Here's a reproducible example.
library(data.table)
append.log<-function(x) {
cat(paste("value:",x,"\n"))
}
idx.dt <- data.table:::`[.data.table`
environment(idx.dt)<-asNamespace("data.table")
idx.wrap <- function(...) {
x<-do.call(idx.dt, as.list(substitute(...())), envir=parent.frame())
append.log(if(is(x, "data.table")) {
nrow(x)
} else { NA })
x
}
environment(idx.wrap)<-asNamespace("data.table")
(unlockBinding)("[.data.table",asNamespace("data.table"))
assign("[.data.table",idx.wrap,envir=asNamespace("data.table"),inherits=FALSE)
dt<-data.table(a=1:10, b=seq(2, 20, by=2), c=letters[1:10])
dt[a%%2==0]
Since R 3.2.0 it is fully possible, thanks to new function returnValue.
Working example below.
f <- function(x, err = FALSE){
pt <- proc.time()[[3L]]
on.exit(message(paste("proc.time:",round(proc.time()[[3L]]-pt,4),"\nnrow:",as.integer(nrow(returnValue()))[1L])))
Sys.sleep(0.001)
if(err) stop("some error")
return(x)
}
dt <- data.frame(a = 1:5, b = letters[1:5])
f(dt)
f(dt, err=T)
f(dt)
f(dt[dt$a %in% 2:3 & dt$b %in% c("c","d"),])
I am hunting for ways to monitor when an object is updated, and do something (e.g. redraw a plot, print a message).
The ideal would be a generic function like:
watch(obj, fn)
where fn is called whenever obj is changed.
Or, are there any equivalents to View or plot which could do this?
makeActiveBinding is just what I was looking for, suggested by Ben Bolker. A quick example:
makeActiveBinding("visibull", function(x) {
if (! missing(x)) {
.invisibull <<- x;
View(.invisibull)
} else .invisibull
}, .GlobalEnv)
visibull <- data.frame(a=1:10, b=1:10)
visibull
visibull$a <- visibull$a + 1
.invisibull
I have a rather complicated situation where I may end up passing a variable that doesn't exist to a function. I have a pretty good idea when this will happen, and fixing it will be difficult; I would be satisfied with being able to detect the condition reliably and executing a workaround in that case. I could simply use
inherits(try(eval(possibly_missing_variable),silent=TRUE),"try-error")
but if possible I would like to test for the specific condition that leads to the error object 'possibly_missing_variable' not found. (I could try to grep for "not found" in the error message, but I have been admonished on the r-devel list in the past that this will fail if R is being run in a different language so that a translated version of the error message appears.)
I've tried various combinations of deparse(substitute(...)), but they don't seem to run far enough up the call stack. Here's my best shot at a reproducible example:
f <- function(d) {
## test here
cat("'d' exists:",exists("d"),"\n") ## TRUE
cat("deparse(substitute(d)):",dd <- deparse(substitute(d)),"\n") ## OK
cat("exists('",dd,"'): ",exists(dd),"\n",sep="")
eval(d)
}
f2 <- function(ddd) {
f(ddd)
}
ddd <- 5
f2(junk)
The results are:
'd' exists: TRUE
deparse(substitute(d)): ddd
exists('ddd'): TRUE
Error in eval(d) : object 'junk' not found
I want a test that will correctly inform me (before hitting the error) that the evaluation will fail because the relevant object can't be found anywhere in the stack of environments/enclosing environments etc.. Any ideas ... ?
More generally, is there a way to figure out the farthest-upstream name of an argument ("junk" in this case)? If I could do that, then exists(farthest_upstream_name) would solve my problem.
Perhaps something like this (stolen from my second answer to this question)?
f <- function(d) {
## test here
ff <- sys.frames()
ex <- substitute(d)
ii <- rev(seq_along(ff))
for(i in ii) {
ex <- eval(substitute(substitute(x, env=sys.frames()[[n]]),
env = list(x = ex, n=i)))
}
if(!exists(deparse(ex))) stop("Substitute real error action here")
eval(d)
}
f2 <- function(ddd) {
f(ddd)
}
ddd <- 5
f2(junk)
## Error in f(ddd) (from #10) : Substitute real error action here
I like Josh O'Brien's approach. However, using f as defined there, the "non-existence" error condition can be triggered even when the passed name does in fact have an active binding, if it was created local to some function within the call stack (rather than in the global environment). Also, though perhaps not specifically relevant to Ben's intended usage, the error will be triggered if the argument is an expression, not just a name.
A simple fix is to tweak Josh's function to include an is.symbol test:
f <- function(d) {
## test here
ff <- sys.frames()
ex <- substitute(d)
ii <- rev(seq_along(ff))
for(i in ii) {
ex <- eval(substitute(substitute(x, env=sys.frames()[[n]]),
env = list(x = ex, n=i)))
}
if(is.symbol(ex) && !exists(deparse(ex))) {
stop("Substitute real error action here")
}
eval(d)
}
The desired check still works:
f2 <- function(ddd) {
f(ddd)
}
f2(junk)
## Error in f(ddd) : Substitute real error action here
But the following two cases now pass through rather than yielding the error:
# case 1: argument to f is local to a calling function
f3 <- function() {
notjunk <- 999
f(notjunk)
}
f3()
## [1] 999
# case 2: argument to f is an expression
f2(5+5)
## [1] 10
What's happening in f is that after the repeated application of call-substitute-substitute, ex is set to the evaluated argument itself in case 1 above, and to the passed (albeit still unevaluated) call in case 2. In both cases, the exists test alone would fail because ex is not actually a name (aka symbol), but clearly non-existence is not a concern if we've been able to resolve beyond the name.
I have a question about function environments in the R language.
I know that everytime a function is called in R, a new environment E
is created in which the function body is executed. The parent link of
E points to the environment in which the function was created.
My question: Is it possible to specify the environment E somehow, i.e., can one
provide a certain environment in which function execution should happen?
A function has an environment that can be changed from outside the function, but not inside the function itself. The environment is a property of the function and can be retrieved/set with environment(). A function has at most one environment, but you can make copies of that function with different environments.
Let's set up some environments with values for x.
x <- 0
a <- new.env(); a$x <- 5
b <- new.env(); b$x <- 10
and a function foo that uses x from the environment
foo <- function(a) {
a + x
}
foo(1)
# [1] 1
Now we can write a helper function that we can use to call a function with any environment.
with_env <- function(f, e=parent.frame()) {
stopifnot(is.function(f))
environment(f) <- e
f
}
This actually returns a new function with a different environment assigned (or it uses the calling environment if unspecified) and we can call that function by just passing parameters. Observe
with_env(foo, a)(1)
# [1] 6
with_env(foo, b)(1)
# [1] 11
foo(1)
# [1] 1
Here's another approach to the problem, taken directly from http://adv-r.had.co.nz/Functional-programming.html
Consider the code
new_counter <- function() {
i <- 0
function() {
i <<- i + 1
i
}
}
(Updated to improve accuracy)
The outer function creates an environment, which is saved as a variable. Calling this variable (a function) effectively calls the inner function, which updates the environment associated with the outer function. (I don't want to directly copy Wickham's entire section on this, but I strongly recommend that anyone interested read the section entitled "Mutable state". I suspect you could get fancier than this. For example, here's a modification with a reset option:
new_counter <- function() {
i <- 0
function(reset = FALSE) {
if(reset) i <<- 0
i <<- i + 1
i
}
}
counter_one <- new_counter()
counter_one()
counter_one()
counter_two <- new_counter()
counter_two()
counter_two()
counter_one(reset = TRUE)
I am not sure I completely track the goal of the question. But one can set the environment that a function executes in, modify the objects in that environment and then reference them from the global environment. Here is an illustrative example, but again I do not know if this answers the questioners question:
e <- new.env()
e$a <- TRUE
testFun <- function(){
print(a)
}
testFun()
Results in: Error in print(a) : object 'a' not found
testFun2 <- function(){
e$a <- !(a)
print(a)
}
environment(testFun2) <- e
testFun2()
Returns: FALSE
e$a
Returns: FALSE