HXA articles

Why exceptions are a bad programming feature

Exceptions are an outgrowth of lack of knowledge, that counter the essence of design, and needlessly add complexity. What they purportedly address in their general usage can be handled by simpler, more basic, means.

1266 words (7 minutes)


What makes an exception not just another return value? That is the key question to reveal the defect here. And the answer is: what makes an exception is what it means elsewhere. Exceptions are defined non-locally, and they connect disparate code together ‒ there is the cause and its misguided consequence. Exceptions are then an overcomplicated practical response to a non-real problem, and so they are net additions of accidental complexity.

Meyer in ‘Object-Oriented Software Construction’ ch 12 defines an exception as the predicament when a function cannot complete in satisfaction of its contract. Varieties of which can be subclassified as:

  • pre/post-condition disappointment;
  • class invariant disappointment;
  • special value (null) encounter;
  • assertion failure;
  • lower system hardware/OS fault.

First, assertions are instrumentation, do not contribute to execution, and are not properly part of the purpose of the software. Lower system faults have an asynchronous/acausal relation with the software, and by definition of that category cannot be integrated in a normal way. Both these cases can be set aside here for needing some very special separate almost extra-linguistic mechanism.

Second, special values like null are really a precondition disappointment. Pre/post-condition disappointment are conditions of a function's interface with its context. So why could we not represent exceptions just in that interface, without adding any new concept or mechanism? Well, the question then is: what distinguishes an exception from an ordinary return type? There is nothing inside the function and the data it produces, so it must be something outside ‒ something external must set the interpretation/meaning of that particular kind of return.

Consider: if a function is called to extract certain data from a source, but the program as a whole simply cannot work if one part is missing, then we might say that missing data, when noted in the extractor, is an exception condition. It seems reasonable to distinguish this case, yet by doing so you now have the design of the called function dependent on its client. This is a frank failure of modularisation, of the basic isolation aimed at and essential to design.

A common simple example is a square-root function confronting a negative param. But you knew all along that square-roots are not defined for negatives, so why would you even write the function to take them? You ignored information and so wrote the wrong software. If the contract cannot be fulfilled, you wrote the wrong contract. Exceptions seem like a phantom concept: they are not really there. From the point of view of the logic, there are no exceptions: everything is as it is specified. The idea that there is a special, important, or critical or fatal kind of result must come from elsewhere ‒ because as far as the function goes, it is just another return value.

One might cast exceptions as something in the execution that was unexpected. This introduces a sense of (un)likeliness as explanation, but the function producing the exception has no such sense of probability: it is defined on its inputs, all of which have equal status. The sense of likeliness or not comes from outside.

Lastly, class invariant disappointment can now be seen as a form of this external context meaning of exceptions, but limited to the bounds of a class. What makes the action of a function ‘wrong’ instead of just another outcome, is what is defined as right or not by the class. But the exception would still propagate out of the class to its caller, so it does not really have a limited bound. And we are back with the same question slightly altered: why is not this just another state of the object (like just another return value) ‒ what makes it a special, an exceptional, state?

One could see exceptions as a patching-up of inadequate compilation. They cordon off certain states and values as not entirely specified, and leave them to be handled later/elsewhere. But acceptance and generalisation of this is a retreat from purposive design.

Of course it cannot be possible for compilation to check everything, that is, to know what every value will be, but it is possible to specify bounds for the range of values. There must be something you can say, or what are you doing at all? It is this balance that exceptions push too far to the deficient side.

Think first of the extreme: if a language has runtime syntax exceptions, then you know ‒ are sure of ‒ nothing about the program. Anything can happen anywhere, because a language like that assumes, and its programmer ought too, that any piece of code can be unboundedly wrong. In those circumstances, whatever sense you have of deliberately programming, of controlling an outcome, is really only a modest probabilistic hope.

Usually it is not so bad, but in dynamically unityped languages there are still a lot of things that can happen to the program that are not clear or specified in the code. So since anything can fail anywhere, stating this locally is informationally empty: when every function return value can also be an error, it is better to make errors implicit ‒ make them exceptions. This is the situation, unfortunately, in much of programming.

Some failures are indeed global, and there always will be some such, though few, and exceptions are at least a faute de mieux response to them. But to take that model for granted, as a default, and to extend and use it more widely as a programming element is a mistake. Really, there should not be any sense of ‘behind-the-scenes’ events: a program is a complete specification of control of the scene. Exceptions are a lack of knowledge, a dressed-up ‘undefined behaviour’, and if they are everywhere, something is very wrong in the means of expression of structure.

In summary, there are two main points to extract.

First, the only substantive definition of exceptions is a non-local one, and that means they are design degenerative. Exceptions complexify a function and interface by involving non-local concerns. The notion of ‘exceptional’, of the ‘unexpected’, is nothing but an avoidance and shortfall of design. Design specifies the entire logic, it states the limits of what is possible. If you do not know what is possible ‒ which is what the uncertainty of ‘exceptional’ is ‒ you have not completed the design. And so to promote exceptions as a structure is to encourage incompleteness in designs.

Second, what we have with all this is that a special case is being made for a programming structure ‒ the exception ‒ and yet this could be handled with other general means. Exceptions are just another return value. Preconditions should be encoded in the parameter types, and postconditions should be encoded in the return types. Then pre/post-condition disappointment is rendered impossible by the type system. And what seemed a moderately plausible concession to the use of exceptions, within class scope, is obviated by the non-imperative state and pure functions of functional programming. Adding an option sum-type and pipeliner can simplify the syntax.

Exceptions are like OO implementation inheritance, on net creating unneeded and excess coupling, and like that should follow the same path of disapproval and abandonment. (It is notable and pleasing to see Rust leading the way, relegating exceptions to a minimal vestige not really meant to be used.). Exceptions are a misconceived and weak programming structure. Your languages should not have them, and your programs should not use them.


Metadata

{ "DC": {
   "title": "Why exceptions are a bad programming feature",
   "creator": "Harrison Ainsworth",

   "date": "2021-05-23",
   "date": "2021-05-06",

   "type": "article",
   "format": "text/html",

   "language": "en-GB",
   "subject": "software, programming",
   "description": "Exceptions are an outgrowth of lack of knowledge, that counter the essence of design, and needlessly add complexity. What they purportedly address in their general usage can be handled by simpler, more basic, means. (1266 words)",

   "identifier": "urn:uuid:3E8C6E1D-736F-4E51-B73C-68F20B9C9BCD",
   "relation": "http://www.hxa.name/articles/content/why-exceptions-are-bad_hxa7241_2021.html",

   "rights": "Creative Commons BY-SA 4.0 License"
} }