Following a discussion over on David MacIver’s blog. I have a piece of functionality I’d like to see in more languages.
The ability to easily add information to exceptions
try { // ... } catch (Exception e) { e.addInformation("serverName", serverName); throw e; }
Or, even better, a way to annotate variables to get this for free/cheap:
@PutInExceptions String serverName = config.getServerName;
The advantage of being able to add this information is that it makes it easier to diagnose exceptions without losing the type of the exception. Often, when we see an exception we get a full stack trace telling us the line that caused it but not enough surrounding information to make replication easy. Consider the following:
public User getUserDetails(int id) { if (!cache.contains(User.class, id)) { // Populate the cache } User user = cache.get(User.class, id); log("Got user with name ["+user.getName()+"]"); return user; }
If this blows up on the log line then all we know is that user is null. We don’t know the id that was used. This can make debugging harder.
Having the ability to annotate things as “add this to any exception that occurs” makes it easy and cheap to get good error handling. If you then want a method to be ultra-fast then you don’t add the annotations – simple.
This is pretty easy to do right now. I added this to scala.util.control.Exception:
def transforming[T](f: Throwable => Throwable)(exceptions: Class[_]*): Catch[T] = catching(exceptions: _*) withApply (ex => throw f(ex))Then:
scala> transforming(x => new Exception("hi mom", x))(classOf[java.io.IOException]) { throw new java.io.IOException("I'm a bug") } java.lang.Exception: hi mom at $anonfun$1.apply(:7) at $anonfun$1.apply(:7) scala> lastException.getCause res1: java.lang.Throwable = java.io.IOException: I'm a bugAnd the ceremony could be lowered as far as you wanted.
I bet the formatting on this is completely unreadable, which is reason #17 why I almost never comment in blogs.
I added pre tags to keep the formatting… but it looks like the CSS is a bit messed up.
Nice trick :)
Henry Ware posted another trick to the scala list:
def addToException[T](msg:String)(block: =>T)={ try { block } catch { case e => throw new Exception(msg,e) }}used as
def loadUser(id:Int)=addToException("id="+id){ ... }