Exceptions vs. Return Values to represent errors (in F#) – Conceptual view

-

Recently I’ve been read­ing nu­mer­ous ar­ti­cles on the age old ques­tion of ex­cep­tions vs. re­turn val­ues. There is a vast lit­er­a­ture on the topic with very pas­sion­ate opin­ions on one side or the other. Below is my view on it.

First of all, I’ll de­fine my terms.

Examples of the above for a FetchUser(userName) func­tion:

The dif­fer­ence be­tween Contingency and Fault is not sharp in prac­tice and re­quires com­mon sense, but it is use­ful none less. When in doubt, it would ap­pear pru­dent to con­sider an event as a Contingency, so that the caller gets a chance to re­cover.

Ideally, you would like a Contingency to be part of the sig­na­ture of a func­tion, so that the caller knows about it. On the other end, a Fault should­n’t be part of the sig­na­ture of a func­tion for two rea­sons:

The above seems to sug­gest that Contingencies should be rep­re­sented as re­turn val­ues and Faults as ex­cep­tions. As an aside, in Java the for­mer is rep­re­sented as checked ex­cep­tions, which is part of the sig­na­ture. We’ll tackle checked ex­cep­tions later on.

An im­por­tant point that is of­ten ne­glected in the dis­cus­sions on this topic is that there are two cat­e­gories of ap­pli­ca­tions: ap­pli­ca­tions that care about Contingencies (Critical apps) and ap­pli­ca­tions that don’t (Normal apps). I am of the opin­ion that the lat­ter cat­e­gory is the largest.

In many cases you can in­deed write just the suc­cess code path and, if any­thing goes wrong, you just clean up af­ter your­self and exit. That is a per­fectly rea­son­able thing to do for very many ap­pli­ca­tions. You are trad­ing off speed of de­vel­op­ment with sta­bil­ity.  Your ap­pli­ca­tion can be any­where on that con­tin­uum.

Examples of Normal apps are: build scripts, util­ity ap­pli­ca­tions, de­part­men­tal ap­pli­ca­tions where you can fix things quickly on the user ma­chine, in­tranet web sites, in­ter­net web sites that are purely in­for­ma­tive, etc …

Examples of Critical apps are: servers, data­bases, op­er­at­ing sys­tems, web site that sell stuff,  etc …

For Normal apps, treat­ing Contingencies as Fault is the right thing to do. You just slap a try … catch around your event loop/ thread/ process and you do your best to get the de­vel­oper to fix the prob­lem quickly. I think a lot of the angst of the return value crowd’ is pred­i­cated on not hav­ing this dis­tinc­tion in mind. They are mak­ing very valid point re­gard­ing Critical apps to a crowd that is think­ing about Normal apps. So the two sides are cross-talk­ing.

Also, in my opin­ion, the main prob­lem with Java checked ex­cep­tions is that they make writ­ing Normal apps as cum­ber­some as writ­ing Critical apps. So, rea­son­ably, peo­ple com­plain.

The .NET frame­work de­cided to use Exceptions as the main way to con­vey both Faults and Contingencies. By do­ing so, it makes it eas­ier to write Normal apps, but more dif­fi­cult to write Critical apps.

For a Critical app or sec­tion of code, you’d want to:

In the next post, let’s see how we can rep­re­sent some of this in F#.

Tags