listen to me explain this in a podcast
TL;DR
Errors are unrecoverable, Exceptions are routine.
Most languages (Java, PHP) build the d...
For further actions, you may consider blocking this person and/or reporting abuse
In JavaScript,
Error
s are just another type of object. So you canreturn new Error('message')
instead of throwing it, and TypeScript will modify the return type accordingly.On the flipside, you can
throw
something that's not anError
if you really want.throw 'just a string'
,throw [error1, error2]
, etc. Though some linter configurations might complain at you if you do this.Good read.
But very one sided. Coin has two sides, there are languages which choose to have try catch and those which treat error flow as just one of flows, not a special language thing. And FP languages exactly do that(Elm or Haskell,Ocaml) instead of pair result, error which you have shown in Go there is Either (most probably you know what it is so will not continue). So Go really go in the se direction here.
So or we just use error as one of possible returns procedure/function can have or error have own special execution by try catch which skips call stack.
What problem I see in returning errors is that there can be many levels of such passing error around until we find a place which deals with that finally. With try catch we can just jump to the place where we handle it.
I am not sure that saying that Error and Exception are different things. Fact that you use some language feature which allows to jump doesn't mean that you cannot make proper error handling from that. I see try catch as just language construct. In many situation we just don't know what to do with some errors, there is just no alternative flow, it failed and game over so we throw and catch in the place where we say game over instead of juggling around and pretending that we know what to do.
Some time ago I also thought error as just return value is always the way to go. Now I see the trade-off in both solutions. But if language design bets on try/catch I would use it.
I love this too about exceptions. When my code mixes the actual logic with the error handling (
if err != nil
and the like), it gets very difficult to read. I like that try/catch lets the happy path flow naturally and you handle exceptions in one place.Of course, you need to remember to handle the errors, but that's a price I'm willing to pay, rather than passing error objects around.
Still reading the article, but this kept bugging me, so I had to comment.😁
Isn't this the other way around? I remember someone (I forget who) saying, "Exceptions should be reserved for exceptional circumstances." As in, if something that's routine happens, you shouldn't throw an exception. Reserve exceptions for situations that shouldn't happen.
I don't know if I'm all in on that. It's something I'm still mulling on. Just had to say it because it seems like you see it differently. Maybe it's just a nomenclature issue.😅
Another thing is: I feel like we may be co-opting language terminology and constructs into our programming model, rather than the other way around. For instance, just because a language says "We don't have exceptions" doesn't mean that's true. If it has some error-handling mechanism that acts identically to exceptions, in most languages, then those are exceptions.
I think distinguishing between "error" and "exception" is a doomed task, because those are defined by the language. I often use them interchangeably. Maybe we should use language-agnostic terms like "failures" vs "errors", where:
In summary: This shit is confusing. 😂
Created a wrapper function to do this:
gist.github.com/devinrhode2/254255...
I think, this
safeCall
is nice when you are calling external libraries/functions that are hard to change to return[thing, error]
.I am currently converting a 4-5 throw statements to:
The nice thing about this, is that the consumer/caller does not need to check:
They can simply:
However, once we destructure, the "Either or ness" disappears. Type of
thing
anderror
are bothThing | undefined
andError | undefined
. While basics like this workThere are instances where you want to pass the whole tuple,
maybeThing
.I'm curious what a good variable name is for this
maybeThing
.Update: I recommend using an object style return. The array brackets are cute, but less useful. Positional args are fragile, and while this is only two, if you want to give a special error code or warning, then a simple
new Error('asdf')
can't represent either of these two (at least not very clearly).So you may typically do:
but you may occassionally:
Note to self: I don't think I have any code that actually uses this idea. Was just a pure idea.
You may be interested in how Pony handles errors
They look like exceptions (try/catch), but they are not. Because you can't actually use exception as value which carries message.
Also this maybe interesting (this is about Go): Rob Pike Reinvented Monads
In JS/TS we also have errors as values (idea copied from Monads in Haskell): io-ts.
You also might be interested in Either monad from fp-ts library.
@olexandrpopov, your Either link is broken, new url is:
rlee.dev/practical-guide-to-fp-ts-...
The import cost of this is insane. More code than an mui
Box
component!A nice article! Thanks for writing up!
My approach to error handling in JS I detailed here:
thedevcoach.co.uk/error-handling-j...
But the TL;DR: is:
Doing this means you never miss any unhandled errors, and gives a very clear pattern for error handling in JS.
But... I prefer go's way of doing things, which becomes essentially the same, but without the high level try/catch boilerplate, etc.
Joe Duffy wrote an excellent lengthy article on Midori's Error Model. The entire article is applicable to this topic, but in particular the Bugs Aren't Recoverable Errors! section.
As you can see in the comments, this is a very confusing issue. More good articles like this are needed.
Yeah, I'm trying to hash out a good pattern: dev.to/devinrhode2/comment/22912
Honestly, the worst thing I see is people using Try/Catch blocks to wrap a dozen potential issues and let it just blindly catch the failures regardless of how they interact with the rest of the system.
Most sites don't have one condition to check, they have half a dozen or more per feature, and ideally you check each one separately to validate the data and then TRY to process it and Catch any failures that result from that [eg. the "should never happen" conditions], while also handling the response data correctly to display any data errors to the users.
Personally I avoid using Try/Catch as much as I can and instead validate variables using conditionals and either manually logging errors or generating error messages to the user as needed. The manual logging is rare as again if you're catching errors like that, more often than not the only reason you'd log it is to see how someone is trying to abuse your code since you already know what the issue is and it's not really a problem that ever happens in a normal use-case. The best approach to someone trying to break into your system is to respond with a vague non-issue like "invalid token"...whether you use a token or not, the would-be hacker doesn't deserver a real response.
Exceptions can happen, but basic Errors should never be present in production code. If there are basic Errors in production it means you didn't test your work by actually running it/using it, I'm not talking about using the stupid linters that complain about semi-colons and 80 characters per line limits. I mean, really test your work to know it works correctly. You have [or have been given] requirements, if you don't, demand or write them, and meet them.
What's your vs code theme?