
Errors are values - davecheney
http://blog.golang.org/errors-are-values
======
chrismorgan
What in Go is a return type of (Anything, error) with typically exactly one of
the two being nil is in Rust the Result type, `enum Result<T, E> { Ok(T),
Err(E) }`, something which is explicitly exclusively-either an OK T or an
error E.

The equivalent of `x, err = expr; if err != nil { return err }` is _very_
common in Rust; it would be written `x = match expr { Ok(x) => x, Err(e) =>
return Err(e) }`. It’s such a common pattern that a macro named try! was
placed in the standard library for this incantation. Therefore you can write
`x = try!(expr);` (or if you’re going to ignore the Ok variant, just
`try!(expr)`).

Rust also warns you if you do nothing with a Result object; you can ignore the
warning or suppress it, but it all makes it very much harder to ignore such
error conditions. Go is one step up from C, where integral error-indicating
return codes are the norm and are typically ignored, but I feel it’s still a
fair way off the confidence that Rust gives me that I haven’t accidentally
forgotten to deal with an error case. If you want people to _get_ the message
“Whatever you do, always check your errors!”, it helps if you design the
language to make it hard for the user to forget.

(You people that know Rust, I’m ignoring the `FromError` part for simplicity.
That’s only in libstd’s try!, anyway; libcore’s try! doesn’t have it.)

~~~
rwcarlsen
In Go I not infrequently make use of a non-nil value AND a non-nil error. A
canonical example of this is the io.Reader interface in Go's standard library
[1]. I think it is a very useful idiom particularly when dealing with things
where failure is more normal - e.g. dealing with network services, disk IO,
decoding partially corrupt exif data from images. Often you want a best-effort
at the value even if you run into problems.

[1] [http://golang.org/pkg/io/#Reader](http://golang.org/pkg/io/#Reader)

~~~
pcwalton
That can be done by either returning a tuple or providing a way to get a
partial value out of your error type. (Result is generic over the error type,
so you can add whatever extra data you'd like to your errors without forcing
your callers to downcast.)

------
rtpg
>We recently scanned all the open source projects we could find and discovered
that this snippet occurs only once per page or two, less often than some would
have you believe.

once per page? that's once per 30 or so lines. 2 or 3 percent of the code is
this same repetitive structure? Also, only takes one time to forget this for
everything to fall apart.

We've spent so much time trying to deal with uninit'd pointers and things like
that on the language level, and here we have something that's much simpler,
yet super repetitive and should be easy to catch on compile time.

Maybe I'm just a bad programmer, but I really prefer the Java "force you to
catch exception" model much more to this.

>It's worth stressing that whatever the design, it's critical that the program
check the errors however they are exposed.

If it's critical to check the errors, I think that it's worth writing language
rules in a way to force people to write a "has error " code path (think
pattern matching).

I get go is trying to be as small of a core as possible, but I think there's a
lot of value in extra compiler checks (with no runtime cost!) to catch this
sort of thing.

~~~
bsdetector
> that's once per 30 or so lines. 2 or 3 percent of the code ... I really
> prefer the Java "force you to catch exception" model much more to this.

And that 2%-3% doesn't include where errors _should_ have been checked but
were not. For instance run programs without stdout ("./program 1>&-") and most
Go console programs will just happily write to nowhere whereas almost all Java
ones error out immediately. Closing stdout doesn't often matter, but sometimes
things like this are important.

I don't understand how in the 2010s it's okay to make a language with worse
reliability. And worse "JavaDoc" equivalent. Go should be better than C and
Java in every category and it's just not better in way too many.

~~~
sangnoir
Language design is about trade-offs. Broadly speaking: being good in one
category means being bad in another.

~~~
TheDong
While your comment is sort of true, it's not applicable here really.

We know how to handle errors better than this. Go's error handling is C style.
We've since found exceptions, Maybes, and friends which are very powerful.

What you said implies that when Rust chooses to use Maybes they're somehow
being bad at something. That's simply false; computer science as a field has
advanced and one of these advancements (not even that recent of one) is
improved understandings of how strong type systems can be used to make code,
including errors, safer. Go completely ignores this of course and they're not
doing it for a tradeoff that I can see. They gain nothing in terms of
performance for not having this type (everything is already a reflect/Value
anyways, so much for not having the baggage that you'd need for generics) and
everything in terms of having a worse language.

Being good at something doesn't instantly mean you're bad at something else.
In fact, I wouldn't even claim that's the general case.

If you disagree, you're welcome to point out the tradeoffs at work here. I
think your comment is just a vaguery that is completely untrue in this
instance.

~~~
enneff
The corresponding loss to Rust's gain is language complexity.

~~~
kibwen
I think this argument is depressingly uncharitable.

One of Rust's goals was to remove the "feature" of null pointers. In its place
rose the feature of tagged unions, to support the `Option` type. This is
strictly trading one feature for another. Once this necessary task was
completed, Rust was happily able to reap all the benefits of tagged unions
elsewhere, such as with the `Result` type. It is not greater language
complexity, it is merely the reuse of simple language features to empower
developers to create more robust software.

Now, I am not among those saying that Go needs tagged unions, or any other
feature for that matter (no, not even _that_ one). I think Go is a good
language, sometimes to the surprise of my peers. But what's disheartening to
me is seeing a Go core developer imply that Go is literally the baseline for
programming language simplicity, in effect papering over all the various
tradeoffs that programming language design entails.

~~~
enneff
You mischaracterised my point. Go is hardly the baseline for language
simplicity. We just made a different set of trade offs to the Rust team, and I
believe Go is the simpler (not simplest) language.

------
overgard
I get being against exceptions in a language like C++ where it can leave your
program in a really weird state and have some ugly performance implications,
but I don't get the argument in languages where it can work just fine.

The argument seems to be "you should be checking for errors anyway", but I
feel like there are basically two types of errors: the obvious kind that
you're going to check for anyway (or get bug reports), like network
disconnects and bad files, and the "how the hell did that even happen"
exception. For the obvious exceptions it seems like a wash, other than the try
catch block being a bit cleaner, but for the "hell the hell did that happen"
error exceptions seem way more useful. I'd much rather my program explode
immediately than risk someone letting the error passing silently because
people didn't check err because like "that will never happen". I know,
functions can call panic and all that, but it just seems clumsy for no net
gain.

I do think there's one type of error checking that doesn't get enough love (I
don't know if Go has this, but I'm guessing probably?). "assert" that gets
removed in release builds. It's kind of silly when you're working alone, but
in a team of programmers or for libraries it's awesome, because it's basically
like runtime documentation that gets compiled out. So you can warn your fellow
devs about things without it bloating your program. The newer C++ has a really
great feature called "static_assert", which is basically a compile time
assertion, which can be handy for things like checking in various places that
maybe if the count in enum changed, that you accounted for it in another spot
of the code, or that certain aspects of a new platform sit right, or things
like that.

~~~
deathanatos
> I get being against exceptions in a language like C++ where it can leave
> your program in a really weird state

If you're programming in modern C++, then exceptions allow you cleanly (as in,
clean code) leave your program in a very well defined state. You have to
adhere to RAII, and there's a lot of C++ shops out there that don't allow RAII
(and are wary of exceptions, because they ignore RAII). I've routinely used
exceptions in C++ as a both efficient and clean way to propagate errors out to
a stack frame that can handle them, fully confident that RAII will clean up as
I go.

> and have some ugly performance implications

More than having an error object that the calling frame needs to check? If I
understand Go (and, since it's mentioned here and I think is relevant, Rust's)
setup, each stack frame between the frame that can handle the error and the
one where the error occurred must check that an error did indeed occur, and if
so return it. Rust makes this pretty through macros, and the article has some
good thoughts on it for Go. Nonetheless, can the compiler compile that to
better than an `if error { return error }` type check?

My understand of C++ exceptions is that this was basically how things _were_
done (but no longer). Previously, exceptions were managed by "sjlj" (short for
setjmp, longjmp, the calls used to manage exceptions); any frame that _could_
have an exception would need to call setjmp on entry, and this was a bit
hefty.

However, my understanding is that, at least for g++ on Linux, C++ has
abandoned this in favor of a newer scheme called "dwarf2"[1]; this scheme is
such that entering a "try" (or a stack frame where RAII would need to do
actual cleanup) is free (rather, it is covered as part of the normal stack
setup, really), leaving it gracefully is also free; only during actual stack
unwinding do exceptions add code/performance penalties.

I think there is an unfortunate amount of information that C++ exceptions are
inefficient left over from the sjlj days.

[1]
[http://www.x86-64.org/documentation/abi.pdf](http://www.x86-64.org/documentation/abi.pdf)

~~~
Rusky
Entering a try with newer implementations of exceptions is free in that it
doesn't execute anything extra, but it's not free. It inhibits the optimizer
and creates a lot of duplicated code in the binary, as well as forcing
exception safety on the programmer, which is pretty hard to achieve
completely, efficiently, and/or cleanly.

------
wyager
Rather, (in Go) errors are values that violate type safety and destroy any
correctness guarantees the compiler makes.

Languages like Rust and Haskell do this right. Both make heavy use of
optionals for failable functions, and Haskell's support for HKTs (and
therefore Monads) allows awesome stuff like Maybe or Either chaining.

Monads allow you to write code that, syntactically, looks like you're not
doing any error handling. In reality, the error handling semantics are baked
in at the Monad level. This makes sense, because error handling is usually
very repetitive (e.g. "Check if the function we just called failed. If it did,
return early. Otherwise, keep going." ad infinitum). As a (pseudcoded)
example:

    
    
        parseTimeString :: String -> Either Error Time
        parseTimeString string = parse string $ do
                hours <- readHours
                semicolon
                minutes <- readMinutes
                semicolon
                seconds <- readSeconds
                return (Time hours minutes seconds)
    

If any of those steps fails, the entire computation will stop and the error
message will be returned. This is roughly equivalent to checking for "nil" (in
Go) after every function call, except you don't actually have to do it. This
is defined in the code for the Either monad, which is at
[https://hackage.haskell.org/package/base-4.7.0.2/docs/src/Da...](https://hackage.haskell.org/package/base-4.7.0.2/docs/src/Data-
Either.html#Either) . As you can see, it's 4 lines of code.

(Side note: this is pretty much how you write parsers in Haskell using the
Parsec library)

~~~
JadeNB
> Both make heavy use of optionals for failable functions ….

Haskell doesn't do so enough, though (I don't know Rust and so won't comment),
so that we still need manually to choose to use the safe library
([http://hackage.haskell.org/package/safe](http://hackage.haskell.org/package/safe)).

~~~
tome
Yes, some unfortunate historical choices there.

~~~
JadeNB
> Yes, some unfortunate historical choices there.

Do you know anything about the motivation of those choices? Surely it won't
have escaped anyone's notice, especially not the designers', that this is a
wart on the language, and that there is an easy solution; so I can only
imagine that the easy solution was rejected for its inelegance—which seems
strange, given the devotion to theoretical purity of the rest of the language.
(I contrast the situation here to that of the numeric tower, which is clearly
flawed but for which, even now, the 'right' approach is not so clear.)

------
inconshreveable
I disagree.

Of the various approaches I've seen to cope with this problem, Rust's try!
macro is the most elegant.

Given the practical minded nature of Go and the core team's willingness to add
in special cases to the language where appropriate, I think a Go version of
try!() would be welcome and fit with the ethos of Go. It solves the 80% case
and it doesn't mean the creation of a type-safe macro system.

The thesis of the blog post is that errors are values and thus you can program
around the repetitive handling of them. But the proposed solution of writing a
one-off implementation of Maybe like `errWriter` isn't an answer because:

1) Most importantly, when you need to call 3 functions which return
differently typed values and can fail, there's nothing in the language to help
you. This is the common case. 2) The implementation is now less obvious. To
understand it, I need to read the `errWriter` helper code. 3) The code space
you'll save by writing a new Maybe type is negligible.

~~~
falcolas
try! isn't too hard to emulate in Go, simply write a wrapper that panic()'s on
and error and passes the first value through.

The rust Result idiom is easy to implement as well, and is used frequently in
the database.sql package to handle nulls.

In this way, rust and Go are not that different. The biggest difference is the
lack of a generic Result, which means you have a NullString struct with a
Valid and String method.

~~~
Jweb_Guru
> try! isn't too hard to emulate in Go, simply write a wrapper that panic()'s
> on and error and passes the first value through.

That isn't what try! does in Rust. You are thinking of unwrap().

~~~
falcolas
You're right. Thanks!

------
modernserf
This looks suspiciously like implementing Maybe every time you need to deal
with a series of errors.

~~~
skybrian
Maybe is not an error-handling mechanism because it doesn't report any error
message. I don't know why Haskell programmers think it's any good for
reporting errors.

You could use Either, but it doesn't give you any guarantee that you'll get a
string back, and the naming is lousy. (Left and Right? Really? I know there's
a mnemomic, but we can do better than that.)

So, might as write your own ADT and name it right.

(Also, a real error-reporting mechanism should support cause chaining so you
can see the dependencies; you can sort of do it with string concatenation but
it gets awkward after a few levels.)

~~~
codygman
> You could use Either, but it doesn't give you any guarantee that you'll get
> a string back

1\. It's better not to use strings and instead use a user defined error type.
2\. What do you mean "doesn't give you any guarantee you'll get a string
back"?

> real error-reporting mechanism should support cause chaining so you can see
> the dependencies;

Can you elaborate on cause chaining, perhaps with an example?

~~~
skybrian
What I mean is something like a stack trace except without all the extra
detail:

    
    
      - Can't persist shopping cart
      - RPC request failed: http://example.com/update_cart
      - Can't write to database "carts"
      - Can't save file: "/a/b/c"
      - OS error: disk full
    

Ideally, all errors in the cause chain should be logged, and they typically
cross abstraction boundaries; the message at each level comes from a different
part of the stack. This seems hard to do across languages, but within a
language there should be a standard way to do it.

(Go falls down on this too. Perhaps we don't need Java-style stack traces that
go on for multiple screens, but we should have more than a string.)

------
latch
I'm a Go developer, but I don't buy this. At best it's showing how, with a bit
of work (often times by the developer), errors aren't that bad. But I don't
see how it's showing that it's better?

If you log the error when checking if ew.err != nil, which might be many lines
or even a different function, you'll have a hard time getting a meaningful
context (which is hard to do in Go in the best case), like a stack.

~~~
enneff
The point is not to use this exact pattern, but to write code to handle errors
in a way that works in your context. If the error check is many lines away or
in a different function, you should use a different approach.

------
sepeth
I think Peter Norvig's quote "Design patterns are bug reports against your
programming language" is a perfect fit for this situation.

~~~
Argorak
I'd really like to see a reputable source for this statement. All I can find
are people attributing that quote to Peter Norvig.

What I can find, is this:
[http://c2.com/cgi/wiki?AreDesignPatternsMissingLanguageFeatu...](http://c2.com/cgi/wiki?AreDesignPatternsMissingLanguageFeatures)

~~~
kyrra
Looks like it's from a slide deck[0] (maybe a presentation) he did. I know
it's not the exact quote, but he may have said it in a talk?

(slide 4) "What Are Design Patterns: To avoid limitations of implementation
language"

[0] [http://norvig.com/design-patterns/](http://norvig.com/design-patterns/)

~~~
kyrra
Looks like the site is down now? Archive.org link for it:

[https://web.archive.org/web/20141224143931/http://norvig.com...](https://web.archive.org/web/20141224143931/http://norvig.com/design-
patterns/)

------
twotwotwo
The stdlib's compress/bzip2 internally does the same sort of thing where you
do a bunch of operations and then check for errors:
[http://golang.org/src/compress/bzip2/bit_reader.go#L12](http://golang.org/src/compress/bzip2/bit_reader.go#L12)

And regexp uses panics to propagate parse errors up the stack internally, and
that's even described in Effective Go:
[http://golang.org/doc/effective_go.html#recover](http://golang.org/doc/effective_go.html#recover)

In both cases an error value is returned to the package user.

In general, I think you should feel freer do abnormal things that suit your
situation _within_ a package as opposed to across API boundaries. The standard
ways of doing things should still be preferred when it's not too costly: the
standards are what they are for a reason (even if the tradeoffs they represent
are more appealing in some situations than others), it's easier to read code
that's written in the way everyone's used to, and standards help keep you from
spending time bikeshedding unimportant details. But the surface area of your
package is the first thing users will have to learn, and what you have to keep
in your head whenever maintaining code that uses your package--that's what
it's most important to make clean and normal-looking and consistent.

I know the post talks about using an unusual error-handling pattern in an API,
and you can do that sometimes, too. Just trying to make a separate point--when
you're thinking about doing unusual stuff, keep in mind there's a big
difference between an unusual implementation and an unusual API.

~~~
Rapzid
Panics can be incredibly useful. I use them in HTTP services to simplify
returning error JSON objects to the consumer.

In general I'm a bit surprised about all the hate(I say hate because of how
people react to it and react to suggestions for how to better work with it)
the error system gets. I find that creative use of error/panic can usually
scratch my "this is too much error handling code" itch when it flares up. As
long as it's documented in the API I agree that unique public error handling
strategies are not all bad.

Interestingly I write a lot of C#, which of course has try/catch/finally, and
I find that if you are doing it properly you have many levels of handling and
a strategy involved at the different levels for what should done when. I find
that many people write TERRIBLE error handling. There may be try/catch
sprinkled around but there will be no strategy to it with edge-cases and
improper handling abound(masked exceptions, finaly's throwing new, unrelated
exceptions, etc). I personally find the amount of effort required into
developing and executing a robust error handling strategy to be about the same
between C# and Go. That's not to say that I don't sometimes wish Go had C#'s
error handling( ;) ), but I realize they are different beasts with different
masters.

------
nbevans
This article is bizarre. I've read it twice expecting an actual solution to
null checking basically everywhere and it doesn't actually present one.

I'll stick with discriminated unions in F#, thanks. Errors are values,
correct. But it seems hardly any languages actually bother to grok the
concept. If your language doesn't provide type safety for ensuring that you
capture all resulting conditions then it has failed its job.

~~~
woah
Somehow the vast majority of code is written in broken languages?

~~~
nbevans
Yes. Along with the vast majority of programmers being absolutely terrible at
their job, to such an extent that they can't even choose an appropriate tool
(language) to do their trade with.

Just look at how OpenSSL is written in C++. And look how that turned out.
OpenSSL indeed. Wide-fucking-openSSL would be an even better name. Open to OSS
contributions it is not; because the codebase is in such a terrible state.
What is the point in OSS if nobody can contribute because it would take a year
just to understand the ridiculousness of the codebase, right down to trying to
understand why they chose such an inappropriate language for creating a
supposedly verifiably secure software system.

------
Arnavion
That second-last example with errWriter destroys safety AFAICT, since it
requires the user to remember to check for ew.err to see if the whole sequence
of operations actually succeeded or not.

Atleast the last example with flush returning the error made sense, since
that's an explicit "commit" operation that the user must call for correctness.
However not every series of operations has a final commit operation, so this
technique is unsafe in general.

(In fact even flush might be optional? I didn't check the docs.)

Edit: The docs for flush say nothing, but the docs for the Writer interface
itself (
[http://golang.org/pkg/bufio/#Writer](http://golang.org/pkg/bufio/#Writer) )
do:

> After all data has been written, the client should call the Flush method to
> guarantee all data has been forwarded to the underlying io.Writer.

~~~
enneff
> That second-last example with errWriter destroys safety AFAICT

It is possible to make mistakes. Go isn't the kind of language that forces you
to be safe and do the right thing all the time. Like all languages, it makes
tradeoffs.

------
Animats
Go lacks a construct comparable to "with" in Python, or WITH-OPEN-FILE in
Lisp, to guarantee that things get closed out on scope exit. Go has "defer",
but that's a clunky replacement. You have to explicitly write the "defer", and
it's always function-scope, not construct-scope.

Most of the problems with exceptions in C++ come from the C++ memory model -
allocation and deallocation in exceptions are usually painful, and care must
be taken to clean up and unlock things. Go doesn't have that problem - it's
garbage collected.

Go does have "panic", which is Go's answer to "longjmp". People keep trying to
use that as an exception mechanism, which is not a good thing.

Python seems to have the best track record with exceptions. Python's usual
problem with exceptions comes from library functions which, under some rare
circumstance, raise an unexpected exception and take down the whole program.
This is mostly a legacy problem due to the poor original design of Python's
exception hierarchy. You would like, for example, for everything that can
possibly go wrong with an HTTP read or a network operation to be a subclass of
EnvironmentError, as caused by an external event or data. That wasn't the case
originally. There are still problems in Python 2.7 with getting a ValueError
because some low-level data item was bad UTF8 or something like that.

The trouble with the "errors are values" concept is that error details tend to
get lost as errors propagate upward. This came up yesterday on HN in
connection with network errors for GoGo's airborne networking service. I once
argued that D's error type should have a "why" pointer slot, so that, when you
passed an error upward, you could link the lower-level error to the higher
level error. The error message printer would then list out the errors,
yielding something like

    
    
        Unable to complete transaction
        because database update failed 
        because database connection was lost
        because of network error "Host down".

~~~
mercurial
> Go lacks a construct comparable to "with" in Python, or WITH-OPEN-FILE in
> Lisp, to guarantee that things get closed out on scope exit. Go has "defer",
> but that's a clunky replacement. You have to explicitly write the "defer",
> and it's always function-scope, not construct-scope.

The problem of avoiding resource leaks is somewhat orthogonal to error
handling, though. Do note that Python's "with" statement doesn't play well
with "yield", which is an important gotcha. But in practice, it's quite
convenient.

------
jimjimjim
"I was wrong about Gopher's Law. The probability of mentioning generics in a
#golang discussion starts at 1 and raises with time." \- Dave Cheney.

Article posted 1 hour ago. Ctrl-F Generics = 3 matches

~~~
ekimekim
True, but I fail to see why it's relevant. Every mention of generic here (at
time of reading) is along the lines of "this pattern could be generalised if
we had generics", which IMO is a fair point to make when discussing ways to
make a repetitive design pattern less painful.

------
harryh
If a function be advertised to return an error code in the event of
difficulties, thou shalt check for that code, yea, even though the checks
triple the size of thy code and produce aches in thy typing fingers, for if
thou thinkest ``it cannot happen to me'', the gods shall surely punish thee
for thy arrogance.

~~~
joshuaellinger
Upvoted -- you got the tone of go exactly right. We're headed back to C#.
Don't like religion in politics or programming languages.

~~~
harryh
It's actually a quote from this document which is very very old:

[http://www.lysator.liu.se/c/ten-
commandments.html](http://www.lysator.liu.se/c/ten-commandments.html)

------
chrisfarms
One thing I love about the repetitive err pattern is that I find my self
wrapping/appending errors with useful context more frequently (rather than say
catching an exception five frames further down and just appending "mylib:
oops").

I realise the later was just laziness on my part, but the constant err
checking really does force good habits on me, which of course yields higher
quality code that's easier to reason about when something goes wrong.

~~~
rdtsc
> (rather than say catching an exception five frames further down and just
> appending "mylib: oops").

That is why I like exceptions, so I don't have to explain possible errors like
this if I don't have to, but can get a top level exception and see a
stacktrace with it. Then choose to add aditional info.

I really like that pattern and use it quite a bit. For example, it is easy to
turn code that connect into a piece of code that re-connects with a back-off
without having to modify lower layers of the connect call itself (say because
it is already in a library).

------
thegeomaster
Lua uses this to handle errors---I don't find it really tedious. A common
pattern is kind of the same---to return either one return value and nil and
then an error message or an error code (Lua, like Go, has multiple returns) in
case of errors. This results in code like

    
    
        let res, err = some_function()
        if res == nil then -- you can also check for err ~= nil like in Go
           -- return from this function with something, err contains more
           -- information
        end
    

I think it's very neat---you can also return nil, err from this function if
you can't deal with it and some code up the stack should be able to handle it
in a meaningful manner. I don't find it particularly tedious; i.e. not less
tedious than writing try..catch blocks, and you can clearly see where are the
function exit points, whereas with exceptions, any line can theoretically
throw an exception. A missed error check can be troublesome, but generally
using nil as a value further down in the function will hopefully trigger
another error which might be then caught so you can spot the bug.

Lisp's continuations are also IMHO a great mechanism---you catch the error and
then you can tell the function down the stack what it should do: you can
abort, or you can try fixing the error and continuing, or you can try again
for some number of times and then report a new error if that fails. It's more
flexible than stuff I've heard of before.

~~~
shiro
Just curious---doesn't that pattern inhibit you to return nil as a meaningful,
valid result? Or is nil treated as invalid value in Lua culture?

~~~
kazagistar
Neither. It just basically never happens that you want both error reporting
and a maybe (nil or value) from the same function. Not in list? Nil. Could not
parse? Nil. Iteration ended? Nil.

------
artursapek
I don't know if I'm just ignorant, but I like that Go forces me to use real
estate for dealing with errors. It's the first language I've used where I give
as much attention and design to the "sad paths" as I do to the "happy path".
And I'm very comfortable navigating through text files and folding blocks of
code, so it doesn't bother me if handling an error costs me a few lines.

Macros like try! might take up less space, but is that really a bottleneck for
your work?

~~~
TheDong
That sounds incredibly ignorant to me.

If this is the first language you've used where you give that sort of
attention to errors, you must not have used and learned from the terrible
mistakes of C, which constantly returns errors which are not required to be
handled, just like go.

Heck, you must not have learned from bash where every second line is "if [[ $?
!= 0 ]]" or you just did set -e and fail hard.

For me, the problem with Go's errors isn't even the lines they take up, but
the fact that they still don't accomplish anything more than C's errors.

Since C, we've learned that you can use exceptions to force errors to be
handled, or Options + matching to ensure each case is handled or extremely
explicitly ignored. We've learned that errors can have contexts like call
stacks attached to them.

Go ignores all of that and goes back to the C way. It doesn't provide an easy
way to chain errors or force them to be handled. It doesn't provide a good
idiomatic way to differentiate between the majority of errors from the stdlib
because it's discouraged to have multiple error struct types; rather you're
encouraged to use errors.New and make an un-differentiatable error type with
no good context. It's encouraged to return an err from deep in the stack
without including any additional information.

In fact, Go's even inconsistent in how it uses errors in the stdlib. For
example, if you look at io, you can see the following:
[http://golang.org/pkg/io/#pkg-variables](http://golang.org/pkg/io/#pkg-
variables)

They define their variables as error types which are simply strings. That
means you can compare with the constants (e.g. err == io.EOF), but if you add
any extra info to that err, such as callstack, you can no longer compare with
it. If you look at os, you see "os.IsNotExist(err)" which tells you if an err
is of a given type. If you look at net (
[http://golang.org/pkg/net/#AddrError](http://golang.org/pkg/net/#AddrError) )
you can see they implemented their own Error interface implementations which
you check by doing `if addrErr, ok := err.(AddrError); ok { // handle error
}`.. the fact that the Go authors included three different error patterns in
the stdlib already shows that the entire error process in go is poorly thought
out. In addition, none of the methods allow you significant flexibility in
adding your own info to the error as you pass it up without losing
information.

If you don't like try! in rust, you're free to write your errors out
explicitly, but if try! fits then it's good to use it.

The majority of bugs in code come from people being unable to keep a
sufficient amount of the program state in their head to track program flow
accurately. If half of your program flow is errors, then no wonder. There's an
old principal that each function body should fit, in full, on one screen
because that ensures it can be read without scrolling and reasoned about
fairly easily. Error handling wasting space also wastes thought and makes your
code provably harder to reason about (if you don't think that's true, look at
the incidence of 'mistake checking error return values in c' vs 'mistake
checking error return in haskell' vs 'forgot to either catch or annotate
throwable for exception in java'. Yeah).

~~~
tobz
So what's your suggestion here? It sounds like this person feels more
proficient, more on-the-ball, writing Go.

So, you call them ignorant (incredibly ignorant, at that) because they're not
using a language with exceptions or pattern matching, and they're wasting
they're time on "C-style" error handling and... what? They're ignorant until
they use something else?

You're also drawing a weak conclusion here: if you use Go idomatically, which
requires more lines on your screen for error handling, and a majority of
programming errors are a result of the programmer not being able to keep a
sufficient amount of program state in their head because of code not fitting
neatly on the screen, then using Go leads to more bugs.

Do you have anything that substantiates that?

~~~
lmm
> So, you call them ignorant (incredibly ignorant, at that) because they're
> not using a language with exceptions or pattern matching, and they're
> wasting they're time on "C-style" error handling and... what? They're
> ignorant until they use something else?

Honestly, yes, though I would try to phrase it more constructively. If you've
only seen C-style languages, it's easy to be ignorant of the world out there;
it's easy to try two or three languages and think you know what's going on,
when in fact the languages you know all come from a very similar lineage. Like
learning English, German and Dutch, and then assuming you know the general
principles of human grammar.

If you've tried a language pattern matching and higher kinded types, and found
them to be not useful, then fair enough. But you owe it to yourself to try,
and even if you end up not using the feature, understanding it will make you a
better programmer.

> Do you have anything that substantiates that?

Over many years of research into what causes bugs, there's only one thing
we've ever been able to correlate reliably with number of bugs: number of
lines of code. Even across different languages, the correlation holds:
[http://programmers.stackexchange.com/questions/185660/is-
the...](http://programmers.stackexchange.com/questions/185660/is-the-average-
number-of-bugs-per-loc-the-same-for-different-programming-languag) . So the
only evidence-based way to reduce software errors is to reduce lines of code -
something you can do by switching to a more expressive language.

------
zzzcpan
How about just using beloved tables instead? Maybe even with function literal
to prepare some stuff, if you need to.

    
    
       for _, s := []string {
          p0[a:b],
          p0[c:d],
          // ...
       } {
          _, err = fd.Write(s)
          if err != nil {
             return err
          }
       }
    

As others already implied it could have been cleaner with exceptions, but I
don't think this is that one case where exceptions are particularly important.
There is no recursion yet or anything like that.

------
h8liu
I think explicitly handling error like go is a good thing. Error handling is a
tricky thing that needs thought to get right and should be expressed clearly
in the code. My only complaint is that `if e != nil { return e }` takes 3
lines of code. Could `gofmt` format that into one line with no line breaking,
I am already pretty happy. If they could add a keyword like `ereturn` that
only returns when the last parameter (must be error) is not nil, just as an
alias, I would be totally satisfied.

------
tel
Errors are absolutely values and that's a great perspective for any language
to take. Essentially, you can see an errorful computation as "returning"
either a valid return value as expected or an error. In Go the quotes are
eliminated: that is exactly what happens

    
    
        ret, err = fd.Write(...)
    

Above we have two (initial) possibilities

    
    
        1. The return was error-free, err = nil, and 
           ret is an interesting return value.
        2. The return was errorful, err != nil, err 
           instead details the reason for error as 
           an integer, and ret is nonsense.
    

This is a little challenging because we must be sure to check err in the
second case since otherwise ret is silently meaningless. This is a source of
bugs called "Boolean Blindness".

In a language with Sum types we'd represent this by saying that errorful
computations adjoin an error type to the return type.

    
    
        Int ----> Either Error Int 
    

What's nice is that this automatically ensures the above invariant of err/ret
is held: a value of type Either Error Int is either "Right i" for some valid
Int i or "Left e" for some error value e. The first thing we check is the
handedness of the result and thus know the result of the computation.

Sum types like Either _solve_ Boolean Blindness because we literally cannot
get access to our return type Int unless it is valid: if we find that the
result is "Left e" then all we have is an Error value.

(Ultimately what this boils down to is that product types like (ret, err)
behave _very_ differently from sum types and cannot replace them.)

If Go had generics we would also notice that the error-passing behavior of
Either is naturally ignorant of the particular choice of types Error and
ain't. We could manipulate it meaningfully (and indeed write the functionality
used in this article) for any and all values e and r as the type Either e r.
Then upon use Either e r would specialize to Either Error Int.

It's easy to argue that this is silly: we only need one error type and it is
not difficult to replicate this code. I'll argue for now that only having one
error type is at best confusing and at worst a lie. Different processes and
functions have different exceptional states. Coercing all of this _variation_
in error meaning into the same error type ensures that misunderstandings and
accidental unifications will occur.

Honestly, I really love Go for making sure that errors were values: it is an
incredible step forward for making exceptional cases more reasonable.
Unfortunately, Go does not seem expressive enough to build the machinery which
makes this style of error handling all of reasonable, non-repetitive, and non-
confusing.

------
mercurial
"Error are values", OK, but as a non-Go programmer, how do you handle
idiomatically the most common case of wanting to bubble the error up the stack
and turn it into something human-readable? Bonus if you do not break
encapsulation. Double bonus if you get a stack trace out of it.

~~~
tomblomfield
panic, recover, runtime.Stack

------
callesgg
The handling of errors in go is weird if one only returns or breaks on errors.
But it works fine if one does something on error, in almost every error check
in my go code I have a log.Println with a human readable text like "could not
read from the application configuration file." much nicer to get errors like
that than a stack dump and a message like could not read from file $filename.

------
al2o3cr
"There is one significant drawback to this approach, at least for some
applications: there is no way to know how much of the processing completed
before the error occurred. If that information is important, a more fine-
grained approach is necessary."

A more fine-grained approach which is conveniently omitted from the article,
BECAUSE IT'S SPAMMING `if err != nil` EVERY OTHER STATEMENT...

~~~
NateDad
This is required for fine grained control of exceptions too. You have to wrap
each statement in a try/catch which is even more verbose than the error check
in go. Otherwise you can't programmatically tell which statement failed.

~~~
prodigal_erik
Java has been recording stack traces for over a decade. This is an argument
for building things into the language, because it would have been very
difficult to roll our own version of it as a library.

------
Terr_
If you want to force the consumer to consider an error case and make _some_
decision about it... Well, I actually like Java's checked exceptions.

You've got to emit an error _somehow_ and static checking ensures the consumer
won't accidentally ignore the case. (Deliberately, perhaps, but that's a
people-problem rather than a technology one.)

------
millstone
I don't get it. How is "errors are values" different from anything? In what
language are errors not values?

~~~
NateDad
In languages where they are exceptions and are not returned, but thrown and
require special syntax to handle.

~~~
millstone
But exceptions are values too, i.e. you routinely have an object of type
IOException. All of the patterns presented in the article can be done easily
with exceptions.

I think maybe "errors are values" is a general principle and not meant to be
specific to Go.

~~~
alecbenzer
Exceptions are values too but they're more than values. "Errors are values"
means "errors are values so just deal with them like you do other values".

------
rvirding
Being an erlang programmer I am so used to programming for the correct case
and not to check errors but let the process crash when you get an error or,
rarely, use a try that I find this style of programming completely wrong. It
is what we are trying to avoid. You will always miss cases.

~~~
TheHydroImpulse
There are many more areas where you simply cannot abort the process on any
error that has been triggered. Erlang, being in the niche of network software,
can get away with this because you can have mechanism that monitor the process
and it's not the end of the world if one process goes down.

Imagine if your Browser crashed every time it hit an error, like a network
connection issue, writing to a file, etc...! That just wouldn't be tolerated
by consumers of the software.

~~~
fenollp
Eh. The point is to not handle things you don't need to. There is of course a
point in handling special cases (like the ones you cited). But compare
Erlang's declarative & to-the-point style with Go's `…,err = …; err != nil`
and you will quickly realise how much defensive programming sucks to read.

------
lectrick
Saying it's so doesn't make it true. You can declare that an error is simply a
special value returned and expect people to check for that value all over the
place, or you can declare that errors are (literally) "exceptions" and should
therefore throw.

~~~
NateDad
You can ignore error values returned just like you can ignore the numeric
value returned from atoi. That's the point. They're just values. You are the
programmer, it's your job to decide what to do with any particular value
returned by a function.

~~~
prodigal_erik
It's also my job to decide what the square root of "fish" should be. What I do
is use a declaration to mechanically verify that cannot ever happen. I never
_ever_ want to silently ignore an error, so why shouldn't I be able to declare
that as well?

------
lazzlazzlazz
Pike's solution does not help very much, and only exposes how little work the
Go compiler is actually doing to ensure your code is safe and sensible.

