So in a relatively famous blog post, Eric Lippert categorizes exceptions as lethal, vexing, bone-headed, and exogenous. He defines vexing exceptions as ones created by unfortunate design decisions and gives the example of Int32.parse from C#. Attempting to parse a user-input string as an integer may well fail, and is in no way exceptional: forcing the programmer to wrap this call in a try/catch block is gratuitous, hence the addition of TryParse.

He then goes on to define exogenous exceptions as exceptions beyond one's control using file IO as an example. But that (at least in modern languages) seems like a design choice: a file opening operation could return filepointer/filereader_object/read_stream on success or null (or some other sentinel value) on failure, with a requisite conditional check afterwards. Better yet, one could return a Maybe of a read_stream (or an Either), and modern functional languages more or less do just that.

Is there an advantage to throwing an exception if a file isn't found vs one of the other options I outlined? Or is this just an unfortunate/legacy design decision?

  • Eric makes it clear in his blog entry that exogenous exceptions are neither bad design choices, nor are they avoidable. "exogenous exceptions appear to be somewhat like vexing exceptions except that they are not the result of unfortunate design choices. Rather, they are the result of untidy external realities impinging upon your beautiful, crisp program logic... Always handle exceptions that indicate unexpected exogenous conditions; generally it is not worthwhile or practical to anticipate every possible failure. Just try the operation and be prepared to handle the exception." – Robert Harvey Oct 12 '16 at 18:08
  • 1
    As to your core question, have a look here: Maybe monad vs exceptions – Robert Harvey Oct 12 '16 at 18:12
  • 3
    There will always be some circumstances outside your control. And they can fail. E.g. in your example, okay, so you return a Maybe<ReadStream>. Opening the file succeeds, so you get a Some<ReadStream> … and then the filesystem driver crashes. – Jörg W Mittag Oct 12 '16 at 18:17
  • @JörgWMittag that's really an answer, and a good one at that. – Jared Smith Oct 12 '16 at 18:22
  • 1
    @RobertHarvey no the point was not as clear to me. The problem with the file operation (unlike the int parse) is that there is potential for failure in many places, trying to check at every one of them would be tedious and error-prone, and it just makes more sense to wrap the entire block of operations in a try/catch. That is the subtlety I was missing but got from Jorg's comment. – Jared Smith Oct 12 '16 at 18:37