Let me be forthright. I am disappointed by the state of main stream programming languages. Disappointed because they make it trivial to write nonsensical programs. They provide programmers with very little guidance on how to write stable, robust applications that are not prone to crash by invalid input. Also they provide very little assurance that code that makes sense today, will still make sense when a library is updated.
A case in point is exception handling in Java, C#, and C++. In these languages, problems mostly arise due to the fact that exceptions subvert the type system. In this post I will be giving a short illustration how the ability to throw exceptions leads to fragile code.
Consider this simple Java class:
There are a number of issues with the
- It is legal to pass a negative
indexas an argument
- It is legal to pass an
indexthat exceeds the array bounds.
In fact, there are probably more integers that are invalid inputs to this function, then there exist valid ones.
These issues are not handled in the code. Maybe that is because it is known beforehand that the method will never be called with a nonsensical
index. But this is not made explicit in the method’s signature. It may be made explicit in the documentation, which compilers and tools do not read and cannot use to aide the programmer, or in the implementation, which in OO-programming should be hidden from external code.
One could modify
getAlpaca to return
null when given an illegal index:
The benefit to this approach is that for any
index, the method will return something. However, it is not a universal solution as, in Java, this approach cannot be used for integers that cannot be null.
Now suppose a software developer creates the following program. This code is perfectly safe given the above definition of
Unfortunately, in a later version of the
getAlpaca method gets modified to throw an exception instead of returning null. The
main method will still compile but now crash at runtime. Thus the exception negates some of the benefits of a statically typed, compiled language.
In the same vein, a programmer relying on this behavior would find her code crashing if the exception throwing method was replaced by the null returning version.
The problem with exceptions is that they usually bypass the type system entirely1. They can be thrown at random points in the code, without the compiler, IDE, or user being aware of the possibility. Exceptions lead to fragile code and hence to fragile software.
Fortunately, there are alternatives which I will explore in future blog posts.
A notable exception is Swift’s type system, which requires both callee and caller to signal they might throw or will catch exceptions respectively.↩