TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Error Handling Patterns

50 pointsby andreabergiaabout 2 years ago

20 comments

gpderettaabout 2 years ago
Obviously sum types for errors are vastly superior to exceptions. Compare the following very readable Error variant pseudocode:<p><pre><code> fn doSomethingFallibly() -&gt; SameVal | Error SomeVal x = ...; if failed: return Error else return x fn doTheThing() -&gt; void match doSomethingFallibly(): SomeVal x: &#x2F;* use x *&#x2F; Error: &#x2F;* log the error *&#x2F; </code></pre> To the utterly unreadable exception-based implementation:<p><pre><code> fn doSomethingFallibly() -&gt; SameVal, except Error SomeVal x = ...; if failed: raise Error else return x fn doTheThing() -&gt; void try Someval x = doSomethingFallibly() &#x2F;* use x *&#x2F; except Error: &#x2F;* log the error *&#x2F; </code></pre> This is especially egregious if you want to propagate the error. For the error variant case you can use a beautiful monadic solution<p><pre><code> fn doTheThing() -&gt; void|Error SomeVal x &lt;- doSomethingFallibly()? </code></pre> While the exception based variant completely obscures the control flow:<p><pre><code> fn doTheThing() -&gt; void, raises Error SomeVal x = try doSomethingFallibly()</code></pre>
评论 #35813463 未加载
评论 #35813385 未加载
评论 #35813498 未加载
评论 #35822583 未加载
评论 #35813776 未加载
gpderettaabout 2 years ago
Obviously exceptions are vastly superior to sum types for errors. Compare the following very readable exception based-pseudocode:<p><pre><code> fn doSomethingFallibly() -&gt; SameVal, except Error SomeVal x = ...; if failed: raise Error else return x fn doTheThing() -&gt; void try Someval x = doSomethingFallibly() &#x2F;* use x *&#x2F; except Error: &#x2F;* log the error *&#x2F; </code></pre> To the utterly unreadable error variant based implementation that uses obscure functional stuff like patter matching:<p><pre><code> fn doSomethingFallibly() -&gt; SameVal | Error SomeVal x = ...; if failed: return Error else return x fn doTheThing() -&gt; void match doSomethingFallibly(): SomeVal x: &#x2F;* use x *&#x2F; Error: &#x2F;* log the error *&#x2F; </code></pre> This is especially egregious if you want to propagate the error. The exception based variant can focus on the success control flow:<p><pre><code> fn doTheThing() -&gt; void, raises Error SomeVal x = try doSomethingFallibly() </code></pre> While for the error variant case you have to use even more obscure monadic code:<p><pre><code> fn doTheThing() -&gt; void|Error SomeVal x &lt;- doSomethingFallibly()?</code></pre>
评论 #35813250 未加载
评论 #35813200 未加载
评论 #35813244 未加载
red_admiralabout 2 years ago
Another take on this is the &quot;Kroll result&quot; after Rachel Kroll of rachelbythebay fame. Basically, it&#x27;s a C++ type &quot;Result&lt;T&gt;&quot; with methods like &quot;bool isError()&quot; and &quot;T get()&quot; or something like that, but the twist is that it contains a private boolean field &quot;checked&quot; initialized to false, but set as a side-effect of calling isError. If you call get() and checked == false, then it blows up with an exception (I presume it does that too if you call get() and it&#x27;s actually an error).<p>That way, if you ever write code that doesn&#x27;t error-check before trying to get the &quot;happy path&quot; result, you&#x27;ll notice immediately, even if it&#x27;s the kind of thing that very rarely fails and you haven&#x27;t written a unit test for it.<p>A chaotic good CS educator might want to try out an experiment: give the students an assignment where they have to work with an API that uses a normal result type, explain why error-checking is really important, but when you&#x27;re marking the assignment switch out the result type for the Kroll one.
评论 #35814166 未加载
评论 #35814066 未加载
theshrike79about 2 years ago
My preference is either a Result-object or the Go convention of (result, error).<p>Both force you to handle the error or explicitly ignore it, which is the correct way. Either you take care of it there if you have the context to figure out if it&#x27;s relevant or not or you explicitly bump it one step up for the caller to handle.
评论 #35813312 未加载
评论 #35813253 未加载
评论 #35813098 未加载
masklinnabout 2 years ago
Not a great article, very little depth, only a shallow mostly syntax-level overview. Notably doesn&#x27;t at all explore the consequences of those choices in how they relate to convenience, (type) safety, composability, interaction with generics, ... beyond merely mentioning the failure of java&#x27;s checked exceptions.<p>It&#x27;s also severely missing in breadth e.g. Swift and Zig use an exception-type <i>syntax</i>, but result-type <i>semantics</i>, it does not cover <i>conditions and restarts</i>, or takes like Common Lisp&#x27;s MRV, where the ancillary return values are treated as secondary and interacted with through special functions.
linkddabout 2 years ago
It&#x27;s missing the Erlang&#x2F;Elixir pattern of returning a tuple `{:ok, T}` or `{:error, E}`, where we can then use pattern matching, or `with` expressions, etc...<p>To be fair, it is very similar to a `Result&lt;T, E&gt;` type, which is why I made this library a while ago: <a href="https:&#x2F;&#x2F;github.com&#x2F;linkdd&#x2F;rustic_result">https:&#x2F;&#x2F;github.com&#x2F;linkdd&#x2F;rustic_result</a>
评论 #35813328 未加载
wafabout 2 years ago
An older yet still excellent take on this topic is <a href="https:&#x2F;&#x2F;joeduffyblog.com&#x2F;2016&#x2F;02&#x2F;07&#x2F;the-error-model&#x2F;" rel="nofollow">https:&#x2F;&#x2F;joeduffyblog.com&#x2F;2016&#x2F;02&#x2F;07&#x2F;the-error-model&#x2F;</a>
评论 #35818286 未加载
divanabout 2 years ago
I&#x27;m writing Go and Dart daily for many years (Go - 9 years, Dart - 4 years). Looking at the Go code (especially old one) I can immediately understand how and where error path is handled. In Dart code it&#x27;s almost never the case – you just hope that it&#x27;s handled somewhere in a right place. If I want to really find this place – it&#x27;s a quest with 10-15 files open. Needless to say I end up inspecting unexpected stacktraces often (rife use of generics also lead to ubiqutuous errors like &quot;type &#x27;Null&#x27; is not a subtype of type &#x27;bool&#x27;&quot; – that&#x27;s with Null safety enabled). I think Dart will be introducing even more features and pattern matching and everything-else-that-exists-in-other-languages. Complexity is piling up more and more around error handling in many languages.<p>Go seems to be the only one holding up the ground of caring about cognitive load on developers and code readability.
mirekrusinabout 2 years ago
Missing:<p>* errors from async-colored functions (ie. .then&#x2F;.catch&#x2F;.finally and higher order combinators on them, ie. we use a lot of p.catch(log.rescue(...)))<p>* errors in generators<p>* errors in async generators<p>Using functional approaches helps working with things like managing severity, error codes, nested errors etc. that may be attached to errors as well as managing higher level concepts like timeouts, retries etc.<p>This article reads like hello world for errors, there is more to it.
js8about 2 years ago
Error handlers, what is called callbacks in the article, have existed since time immaterial in operating systems. They have also been popular in PL&#x2F;I and Lisp as signals and conditions. Unfortunately, Unix implementation kinda limited them (there is limited number of system signals, error handlers do not stack), so they never became really popular in C.
Joker_vDabout 2 years ago
&gt; For example, &quot;printf&quot; in C can fail, but I have not seen many programs checking its return code!<p>Okay, I add<p><pre><code> if (printf(...) &lt; 0) { &#x2F;&#x2F; TODO: Handle error } </code></pre> around the printf call. Now what? How do I handle the error? In most realistic scenarios I can imagine I&#x27;d either just ignore it and keep going, or print an error and abort (but what if printing an error fails too? Oh no...), or I&#x27;ll never actually even get the chance to handle the error because my program will get killed by a SIGPIPE.<p>Seriously, if the printf fails then (barring the malformed format string) that means that the underlying device lost its ability to output data and this ability is most likely <i>not</i> coming back, and your program generally can&#x27;t do anything with it, or even know about something to do: the functions from the FILE*-family abstract the underlying devices <i>extremely</i> well.
评论 #35814266 未加载
评论 #35813909 未加载
cjrabout 2 years ago
Very timely, was just trying to understand how to improve error handling with typescript recently and came across neverthrow (<a href="https:&#x2F;&#x2F;github.com&#x2F;supermacro&#x2F;neverthrow">https:&#x2F;&#x2F;github.com&#x2F;supermacro&#x2F;neverthrow</a>) which looks promising…
评论 #35813186 未加载
评论 #35813238 未加载
hsn915about 2 years ago
There&#x27;s another approach that I don&#x27;t see discussed often: structure operations so that code just &quot;passes through&quot; on errors and code the happy path.<p>In the case of opening a file, you would get an invalid file handle that you can still pass around to functions and nothing would &quot;fail&quot;; it would just be as if you passed a handle to &#x2F;dev&#x2F;null. This scheme requires that you can still explicitly check if the handle is valid.<p>Most people are familir with some form of this with the special NaN floating point value.
评论 #35813502 未加载
评论 #35813220 未加载
red_admiralabout 2 years ago
The monadic&#x2F;Result pattern only works if your language has a lot of syntactic sugar for it (for example to mix functions that can return errors with ones that can&#x27;t you need some kind of &#x27;bind&#x27;), but when you&#x27;re used to it and don&#x27;t try and play too many clever tricks on the side (not _everything_ has to be a monad) then it can be very readable and easy to work with.
notacowardabout 2 years ago
Nice article, and I&#x27;m not just saying that because I wrote something extremely similar myself a few years ago. ;) The one thing I&#x27;d add is that the &quot;defer&quot; construct deserves a mention. It&#x27;s not a comprehensive error handling mechanism by itself, but can often augment or even stand in for one.
KingOfCodersabout 2 years ago
Great that we have more discussions about error handling.<p>Shameless Plug: &quot;Musings about error handling mechanisms in programming languages&quot;<p><a href="https:&#x2F;&#x2F;www.amazingcto.com&#x2F;best-way-to-handle-errors-for-a-programming-language&#x2F;" rel="nofollow">https:&#x2F;&#x2F;www.amazingcto.com&#x2F;best-way-to-handle-errors-for-a-p...</a>
samberabout 2 years ago
For those who would rather use an Either type instead of returning 2 variables in Go, I made this: <a href="https:&#x2F;&#x2F;github.com&#x2F;samber&#x2F;mo">https:&#x2F;&#x2F;github.com&#x2F;samber&#x2F;mo</a>
readthenotes1about 2 years ago
It&#x27;s missing two common error handling techniques:<p>-ignore them -squelch them (&quot;this could never happen&quot;)
geoelkhabout 2 years ago
One of my favorite feature of GoLang
hosejaabout 2 years ago
&gt;Unless you are writing “hello world”<p>Ackshually, ...