5 ms·
I would like to firmly push for a new definition of what an exception is for. It's for _aborting a sub-task in your program, if it cannot complete it's assigned
by agent327 5y ago
I would like to firmly push for a new definition of what an exception is for. It's for _aborting a sub-task in your program, if it cannot complete it's assigned goal_. Unlike the meaningless "it's for exceptional situations", this definition has the benefit of describing what they are for, and when you should use them.
Are you really not expecting an error, even as you are writing code to detect and report on it? No! You think it is unlikely to happen, but you are spending time preparing for it.
But after some errors, whatever sub-task the program was working on just isn't going to happen, and in that case the program needs to get back to a state where it can continue with the next sub-task. Exceptions do precisely that, letting you gracefully back out of the sub-task in a clean and clear manner.
Exceptions are not for aborting your entire program, as some people mistakenly hold (there's abort() for that). The fact that they are comparatively slow doesn't matter. Once you are not going to achieve your goal, it doesn't matter too much whether you'll do so at a rate of a thousand per second or a million per second, except perhaps for total system throughput.
- Chris_Newton 5y agoI would like to firmly push for a new definition of what an exception is for. I have some sympathy with your argument, particularly the idea that “for exceptional situations” is an empty tautology. However, I find it a little strange to characterise exceptions as being “for” any specific purpose. This seems a common theme in the programming community, yet we don’t feel the need to characterise variables or for-loops or function calls as being “for” something in that way. They are just tools that our programming language provides, which have certain behaviour if we use them. That behaviour is (hopefully) defined objectively by the language specification, but how we then employ each tool is an open-ended and subjective question, a matter of judgement or perhaps convention. In the case of exceptions, as provided in most mainstream programming languages, it is objectively true that they immediately exit lower level code and transfer control back up the call stack until they are handled at some higher level (or not). There are at least two reasons we might want to do that: something can’t do its job or something has now done its job. Either way, the outcome for that part of our program is now known and we are ready to proceed accordingly. The proposal in the parent comment, aborting a sub-task if it can’t complete its assigned goal, is in the former camp. This might be the most widespread interpretation of what throwing/raising an exception represents. There is still plenty of debate about whether this should be used for “expected” failures like failing to find a file or connect to a network and/or for “unexpected” failures that imply some logic error in the program itself, but there is a degree of consensus that an exception represents some form of error condition. But then you have languages like Python, which also uses built-in exceptions like StopIteration to indicate routine, successful completion conditions. This might be anathema to the school of thought that says exceptions are “only for indicating exceptional failures”, yet here it is, a different style that is used every day in one of the most popular programming languages ever created, and the sun still came up this morning. Possibly the most unusual idea for using exceptions that I have encountered personally was to indicate a positive outcome from a complicated search algorithm. There were many mutually recursive functions that collectively scanned a graph-like data structure. On identifying a match, an exception would be raised to report the details. Using an exception in this way was like a multi-level early return statement or using a labelled break to exit multiple loops at once. It guaranteed a clean and immediate exit from the search, regardless of how it had recursed to reach that point, and the exception was then neatly handled at the same level of the code that started the recursive search, thus avoiding cluttering one or more paths through every recursive function in that search code with if(done) conditions. To some programmers, this might be a controversial use of the tool, but perhaps the question we should be asking is why, if the code was clearly correct according to the language rules and exceptions provided a neat, easily understood way to solve the design problem.
- tsimionescu 5y agoThere are many ways to write correct programs within the bounds of any programming language. In languages that support goto, you don't even need functions and loops. However, like any human discipline, there are good patterns for how to write a maintainable program, and there are bad patterns. There may be particular cases where a typically bad pattern is nevertheless the best available. But I believe GP is right on a good definition of the most understandable and maintanable reason for exceptions.
- Chris_Newton 5y agoHowever, like any human discipline, there are good patterns for how to write a maintainable program, and there are bad patterns. Sure. What I’m questioning is whether there is any rational, objective basis for arguing that using exceptions to exit early in positive cases is a bad pattern. The programming world is full of opinions, sometimes strongly held by experienced practitioners, that a certain style is bad. The programming world is also full of other experienced practitioners who use some of those controversial styles very successfully. Exceptions are a common source of controversy, but you could just as well look at type systems, significant white space, OOP, functional programming, or a hundred other areas where reasonable people can differ. I believe it’s important to distinguish arguments based on dogma or convention from arguments based on rational logic or empirical evidence. One type helps us to improve as individuals and as a community, while the other can only hold us back.
- agent327 5y agoFWIW, I wouldn't argue it is necessarily a bad pattern, just a highly uncommon one; the sort of thing someone with a lot of experience can do, but that you wouldn't go teaching to beginners. I would also demand to see a comment explaining the unusual usage. What I sort-of expected to be called out on is the definition of what a sub-task really is, because that is still quite vague. I'm inclined to match these to what a user of the program would consider a thing he does with the program at the largest scale level (things like "print a document" or "send a mail"), but there is certainly room for smaller granularity sub-tasks as well. I.e. if you are rendering a web page, but can't display an image for some reason, you'd just abort the image render, not the whole page render. How would I classify the example given (loading a user record from a database)? Well, I don't know! It depends on the context: if we fail at loading that user, are we going to have to give up on whatever other things we were doing as well, or can we continue, possibly with some degraded functionality? If the first, it's an exception. If the second, null (or whatever passes for null in your language of choice, like std::optional in C++). I suppose it wouldn't surprise you too much to learn that I do in fact have database access routines in both styles... There's database.load_one_record ("query"), which throws if it can't find that one thing you are looking for, but also database.load_one_record ("query", default_value), which returns the specified default value if no record matches the query. Because really, this one depends very heavily on context...