Error handling with tryCatch(), get(), St Peter and Frodo Baggins

Active Analytics Ltd: posted 7 May 2013 17:02 by Chibisi Chima-Okereke [ updated 22 May 2013 05:34 ]

Introduction

Before I begin this week's blog entry, I should announce that I am very pleased that my abstract claims reserving using space state models with growth curves has been accepted for the R In Insurance Conference taking place on the 15th of July 2013. The Abstract is located here. The full details of the conference program is located here.

From the title of this blog entry I think you can tell that this is going to be a strange one; I suspect that at times what I am saying will seem like smoke and mirrors, but if you bear with it I think you will find this topic useful.

There are many approaches to handling errors while writing code, if you are creating a library that will be used by many users or you simply want the code you have created not to stop processing when it hits an error, then the try() and tryCatch() functions in R are your friends. In this blog entry, I will be focusing on the tryCatch() function even though what I present can be done using the try() function with subsequent conditionals.

St Peter

I like to think of the tryCatch() function as a filter that prevents certain things from happening, if you imagine that you are running a process over many items and this process takes along time, the last thing you need is for the process to fail and have to manually start the process again or even worse restart the whole show from the beginning. This happens in real situations more than we would like, so if the show must go on, then tryCatch() is your friend. It can act as a filter that keeps errors from stopping your fun. Like St Peter at the gates of heaven, it can keep the bad stuff out.

Frodo Baggins

Now the technique I am describing here takes the filtering effect of the tryCatch() function and gives it steroids. Imagine that you have two functions, one acting as the wrapper for the second. The inner function is a bit like purgatory - anything can happen there, good stuff and bad stuff. When the function is exited it tries to go to the outer function which is heaven. At the gates stands St Peter and if the item is an error he says "You shall not pass!" (I think that may have been someone else).

Now lets say you create a function that you would like to run called Journey().

Journey <- function(bDidFrodoMakeIt)
{
 if(!bDidFrodoMakeIt)
        {
     stop("Frodo cannot return to The Shire")
 }
return(bDidFrodoMakeIt)
}

Evidently, Journey() throws an error if bDidFrodoMakeIt is false. We could construct a tryCatch() block around every Journey() call but maybe instead of just Journey() we have lots of different functions, e.g. JourneyToHelmsDeep(), JourneyToRivendale(), JourneyToRohan() the list goes on. I certainly don't want to spend my time writing tryCatch() for each of those function calls especially if the errors are all handled in a similar way.

Now the wise among you will say "one ring to rule them all", which is precisely how we tackle this. We introduce another function called MyPrecious()

MyPrecious <- function(sFrodo, ...)
{
        funFrodo <- get(sFrodo, mode = "function")
        bToTheShire <- tryCatch(funFrodo(...), error = function(sError){
                sError <- "Oh no Sauron has won, Frodo died in Mordor!"
                cat(sError, "\n")
                return(sError)
        })
 
return(bToTheShire)
}

MyPrecious() acts as an interface using get(). It will run any string entered in the string argument sFrodo as a function as long that function exists; the arguments are passed in the ellipsis. Any errors in the call are taken care of in the tryCatch() block. To run the Journey() function we write:

> Ending <- MyPrecious(sFrodo = "Journey", bDidFrodoMakeIt = FALSE)
Oh no Sauron has won, Frodo died in Mordor!

Note that at this point the tryCatch() has caught the error and prevented it from stopping a running process; the print is just a result of what we cat() to the interpreter.

In reality though, R's error message is often very informative and we would probably like to pass that error message through, so MyPrecious() becomes

MyPrecious <- function(sFrodo, ...)
{
        funFrodo <- get(sFrodo, mode = "function")
        bToTheShire <- tryCatch(funFrodo(...), error = function(sError){
                sError <- paste(paste(sError$call)[1], sError$message, sep = " : ")
                cat(sError, "\n")
                return(sError)
        })
 
return(bToTheShire)
}

And our print becomes

Ending <- MyPrecious(sFrodo = "Journey", bDidFrodoMakeIt = FALSE)
funFrodo : Frodo cannot return to The Shire

That's great but why is this important? It is important because, you don't have to create separate error handling functions at every instance or even for every function. You can confidently access the JourneyToHelmsDeep() function in the same way

MyPrecious(sFrodo = "JourneyToHelmsDeep", ...)

No need to write any more error handling, at worst if you need different error handling for some functions, simply write MyPrecious() equivalents for them.

Conclusion

Error handling is very useful when writing a program that will do serious computation or code that will be used often or by people other than yourself. It is clear that the tryCatch() and get() combination shown here is useful.

There needs to be real thought as to what should and should not be permitted to fail in any process design and what should happen when these events arise.

It is important to understand that writing your functions as I described above will prevent them from stopping even if you use stop() in the inner function; if you actually need a function to fail, you should take this into consideration when writing the error handling. There will be times when you expect a failure and forget that you prevented your function from failing. Even worse is causing a silent error to occur undocumented.

With great power come great responsibility - or words to that effect.

Data Science Consulting & Software Training

Active Analytics Ltd. is a data science consultancy, and Open Source Statistical Software Training company. Please contact us for more details or to comment on the blog.

Dr. Chibisi Chima-Okereke, R Training, Statistics and Data Analysis.