4 ms·
Looking at some other options for comparison: The much-maligned checked exceptions, obviously, _require_ you to have a "catch" block for the exceptions in ques
by ubertaco 3y ago
Looking at some other options for comparison:
The much-maligned checked exceptions, obviously, _require_ you to have a "catch" block for the exceptions in question, or else you get a compiler error.
Option types, Result types, and Either types (which are just generalized Result types) _require_ you to unwrap them explicitly, or else you'll get a compiler error because a Result<T> is not a T.
In Haskell, you've got monadic error-handling inside of do-notation, which is implicit, but at least does the right thing by default of propagating the error back to you, rather than defaulting to swallowing it and moving on.
Meanwhile, in golang, you write this form around 6-7 times in any function of more than a few lines:
result, err := someFunctionCall(input)
if err != nil {
return nil, err
}
...sure, that's so much noise it's hard to miss.....the first time. But since that's literally the only way errors can be handled, you wind up with something more like this:
request, err := readHttpRequest(inputStream)
if err != nil {
return nil, err
}
userSubmission, err := parseUserSubmission(request)
if err != nil {
return nil, err
}
err = validateUserSubmission(userSubmission)
userSubmission = formatAndTruncateMessageText(userSubmission)
submissionTimestamp, err := clock.currentTimeMillis()
if err != nil {
return nil, err
}
insertedId, err := saveUserSubmission(userSubmission, submissionTimestamp)
return formatResponse(userSubmission, insertedId, submissionTimestamp)
...how quickly can you spot the error that was swallowed? How quickly could you spot it at 2am when another, downstream service is broken because its submissions are failing validation but the validation error isn't propagated?
By taking away the typesafety of requiring some sort of type wrapper for multiple return that must be unwrapped, _and_ by taking away the enforcement that you have to check for and either propagate or explicitly swallow the error (by way of a result type or even checked exceptions), Golang takes away your guardrails, leaving you on the mountainside and liable to fall off easily.
By making you do repetitive boilerplate "if err != nil { return nil, err }" every other line or so (rather than providing automatic error-propagation machinery like Haskell's do-notation or Rust's `?` operator), Golang lulls you into "highway hypnosis"[1], setting you up to be much more likely to accidentally drive over the cliff. It makes the Right Thing™ tedious, easily omitted, and only enforced by your own constant vigilance (or complex external tooling that has to guess at your intent), and makes the Wrong Thing™ the default.
[1] https://en.wikipedia.org/wiki/Highway_hypnosis https://en.wikipedia.org/wiki/Highway_hypnosis