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

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

Luca -

☕ 2 min. read

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.

  • Success code path: chunk of code that is re­spon­si­ble to per­form the main task of a func­tion, with­out any re­gard for er­ror con­di­tions
  • Contingency: an event that hap­pens dur­ing the suc­cess code path ex­e­cu­tion that is of in­ter­est for the caller of the func­tion.
    • It hap­pens rarely
    • It can be de­scribed in terms that the caller un­der­stands, it does­n’t re­fer to the im­ple­men­ta­tion of the func­tion
    • The caller stands a chance to re­cover from it
  • Fault: an event that hap­pens dur­ing the suc­cess code path ex­e­cu­tion that is not ex­pected by the caller of the func­tion.
    • It hap­pens rarely
    • It can­not be de­scribed in terms that the caller un­der­stands, it re­quires ref­er­ence to the im­ple­men­ta­tion of the func­tion
    • The caller has no way to re­cover from it

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

  • Success code path: the code that re­trieves the user from the data­base
  • Contingency: the fact that the re­quested user is not in the data­base
  • Fault: user­Name = null, di­vide by zero, stack over­flow, …

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 user can­not re­cover from it
  • Being part of the sig­na­ture would break en­cap­su­la­tion by ex­pos­ing im­ple­men­ta­tion de­tails of the func­tion

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:

  • Express con­tin­gen­cies in the func­tion sig­na­ture
  • Force/Encourage peo­ple to take an ex­plicit de­ci­sion at the call­ing site on how they want to man­age both Contingencies and Faults for each func­tion call

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

0 Webmentions

These are webmentions via the IndieWeb and webmention.io.