Friday, October 26, 2012

Typed Dart Exceptions

‹prev | My Chain | next›

It seems that I was tad premature in thinking that Hipster MVC was ready for Dart Pub. While clicking through the various pieces of my Dart Comics sample app, which is built on Hipster MVC, I came across yet another issue.

Deleting records works just fine, but adding does not quite work. I can open the form, but saving results in an exception in my exception handling:
Internal error: 'http://localhost:3000/scripts/Views.AddComicForm.dart': Error: line 86 pos 22: ')' expected
    catch (Exception e) {
                     ^ 
I am not doing anything fancy with that exception handling code, just logging the exception from the try block:
    try {
      collection.create({
        'title':title.value,
        'author':author.value
      });
    }
    catch (Exception e) {
      print("Exception handled: ${e.type}");
    }
So I could simply remove it, but then I would not learn anything. Checking the M1 release notes, the catch statement was updated so that the exception class is no longer inside the catch. In practice, this means that I can simply remove the exception type from my catch:
    try {
      collection.create({ /* ... */ });
    }
    catch (e) {
      print("Exception handled: ${e.type}");
    }
As-is, I am catching any kind of exception. There is no sense in trying to reproduce the original "limitation" of catching Exception since it is the base class of all exceptions. In other words, that is what I am already doing.

If I wanted to handle a specific kind of exception (like NotImplementedExceptions) differently than the catch-all case, I can use the on keyword:
    // This won't work...
    try {
      throw new NotImplementedException("not done coding");
      // ...
    }
    on NotImplementedException catch (e) {
      print("[not implemented] ${e.type}");
    }
    catch (e) {
      print("Exception handled: ${e.type}");
    }
Except that does not work because there is no type getter on exception classes. I am unsure if the type getter used to exist or if I never actually tried this bit of code. If I wanted to print the type of the class, I can instead use runtimeType:
    try {
      throw new NotImplementedException("not done coding");
      // ...
    }
    on NotImplementedException catch (e) {
      print("[not implemented] ${e.runtimeType}");
    }
    catch (e) {
      print("Exception handled: ${e.runtimeType}");
    }
But really, I may as well just call toString() to get the exception and message:
    try {
      throw new NotImplementedException("not done coding");

      collection.create({
        'title':title.value,
        'author':author.value
      });
    }
    on NotImplementedException catch (e) {
      print("[not implemented] ${e.toString()}");
    }
    catch (e) {
      print("Exception handled: ${e.toString()}");
    }
Which gives me:
[not implemented] NotImplementedException: not done coding
If I change the throw to a different class:
      // ...
      throw new FormatException("not pretty enough");
      // ...
Then the exception falls down to the catch-all:
Exception handled: FormatException: not pretty enough
I think that pretty much covers exceptions. In the end, I revert to the M1 compatible catch-all format since that replicates my original functionality. It is hard to get too excited over try-catch exception handling—I still prefer Ruby's rescue formatting—but this seems to have a reasonable balance of typed exception handling along with familiar syntax.


Day #551

1 comment: