There 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.
However, 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.
FWIW, 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...
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.