I'm curious because I've never felt it being onerous nor felt like there was much friction. Perhaps because I've primarily built web applications and web APIs, it's very common to simply let the exception bubble up to global middleware and handle it at a single point (log, wrap/transform). Then most of the code doesn't really care about exceptions.
The only case where I might add explicit exception handling probably falls into a handful of use cases when there is a desire to a) retry, b) log some local data at the site of failure, c) perform earlier transform before rethrowing up, d) some cleanup, e) discard/ignore it because the exception doesn't matter.
If you need to handle errors, you quickly get into extremely complicated control flow that you now have to test:
// all functions can throw, return nil or not.
// All require cleanup.
try {
a = f();
b = a.g();
} catch(e) {
c = h();
} finally {
if a cleanup_a() // can throw
if b cleanup_b() // null check doesn’t suffice…
if c cleanup_c()
}
Try mapping all the paths through that mess. It’s 6 lines of code. 4 paths can get into the catch block. 8 can get to finally. Finally multiplies that out by some annoying factor.It is just an explicit rendition of a complex topic.
Do you have a more economical example that handles all of the corner cases of cleanup routines throwing errors?