Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to run an expression on.exit() but only if completes normally, not on error?

Tags:

r

I'm aware of the function on.exit in R, which is great. It runs the expression when the calling function exits, either normally or as the result of an error.

What I'd like is for the expression only to be run if the calling function returns normally, but not in the case of an error. I have multiple points where the function could return normally, and multiple points where it could fail. Is there a way to do this?

myfunction = function() {
     ...
     on.exit( if (just exited normally without error) <something> )
     ...
     if (...) then return( point 1 )
     ...
     if (...) then return( point 2 )
     ...
     if (...) then return( point 3 )
     ...
     return ( point 4 )
}
like image 459
Matt Dowle Avatar asked Nov 28 '12 10:11

Matt Dowle


1 Answers

The whole point of on.exit() is exactly to be run regardless of the exit status. Hence it disregards any error signal. This is afaik equivalent to the finally statement of the tryCatch function.

If you want to run code only on normal exit, simply put it at the end of your code. Yes, you'll have to restructure it a bit using else statements and by creating only 1 exit point, but that's considered good coding practice by some.

Using your example, that would be:

myfunction = function() {
     ...
     if (...) then out <- point 1 
     ...
     else if (...) then out <- point 2 
     ...
     else if (...) then out <- point 3 
     ...
     else out <-  point 4 

     WhateverNeedsToRunBeforeReturning

     return(out)
}

Or see the answer of Charles for a nice implementation of this idea using local().

If you insist on using on.exit(), you can gamble on the working of the traceback mechanism to do something like this :

test <- function(x){
  x + 12
}                               

myFun <- function(y){
    on.exit({

        err <- if( exists(".Traceback")){
           nt <- length(.Traceback)        
           .Traceback[[nt]] == sys.calls()[[1]]
        } else {FALSE}

        if(!err) print("test")
    })  
    test(y)
}

.Traceback contains the last call stack resulting in an error. You have to check whether the top call in that stack is equal to the current call, and in that case your call very likely threw the last error. So based on that condition you can try to hack yourself a solution I'd never use myself.

like image 159
Joris Meys Avatar answered Oct 07 '22 01:10

Joris Meys