
Why I’m not leaving Python for Go - ubershmekel
http://uberpython.wordpress.com/2012/09/23/why-im-not-leaving-python-for-go/
======
tptacek
I'm not in love with this aspect of Go either, and I also find that the idiom
for dealing with it (multiple return values and multi-statement if conditional
clauses) doesn't play well with Go's scoping rules, so that I find myself
regularly having to decide between cleaner conditional or an explicit variable
declaration. I also don't love how it makes my code look like my teenage-years
C code.

But I also think this is a very silly reason to adopt or not adopt a tool. No
other approach to error handling is less fraught.

If Python is your only language, Go or something like Go is probably a very
useful thing to have in your back pocket: compiled native binaries, fine-
grained control over memory layout, and a simple and effective concurrency
model is a good thing to have in one package.

There are a lot of things that annoy me about Go (and for that matter Python,
which irritates me for very similar reasons). What I tell myself to get over
that and keep an open mind is, Go may not be my idea of an elegant language
(yet; I'm still learning to appreciate it), but it is an excellent tool. I can
get over the language stuff if the tool works well enough, and Go seems to for
me.

Incidentally, who "leaves" a language? I have a very hard time seeing how,
even from the label on the tin, anyone could believe Go is a great solution
for _every_ problem. Python sure as hell isn't either.

~~~
sausagefeet
> No other approach to error handling is less fraught.

The thing that kills me about Go's error handling is you return error AND
value all the time. It's like an Either that always has Left and Right. I
think it's a bummer, especially coming from a group that has developed
languages in the past.

~~~
emillon
I imagine that this is to keep compatibility with C. And also, this ensures
that when a Left value is accidentally read as a Right one, you don't get a
"random" bit pattern due to type punning, but a correct (albeit meaningless)
value such as NULL.

~~~
sausagefeet
I'm not sure how, C doesn't have multiple return values either. I also don't
really understand the second part. How is that any worse than the current
situation of not checking the error side and reading an undefined (or whatever
it is?) value?

~~~
emillon
Well, the choice is between structures or tagged unions.

I'm not a Go user, but if I understand correctly ("It's like an Either that
always has Left and Right."), functions return a structure with both an error
code and a value.

To wrap a C function into a Go function with this signature, you can
initialize a structure with { code: success; value:f() }; and then of course
error checking has to be done C-style (errno, lib_last_error(), etc).

The problem with tagged unions is that when you access the wrong field (when
it's not enforced by the language), everything can happen : you can build ill-
formed values, jump to random places, etc. Having an explicit NULL in that
cases is better.

In a nutshell, unsafe tagged unions < explicit NULLs < safe tagged unions.

~~~
icebraining
Go actually returns true multiple values, not a structure. You can't assign it
to a single variable, for example, you actually have to assign it to N
variables, with N being the number of values the function returns.

------
cletus
I have mixed feelings about errors as return codes. Then again, I have mixed
feelings about exceptions.

There are two general use cases for exceptions:

1\. Unexpected (typically fatal) problems;

2\. As an alternative to multiple return values.

(1) is things like out of memory errors. (2) is things like you're trying to
parse a user input into a number and it fails. I despise (2) for exceptions.
It means writing code like:

    
    
        try:
          f = float(someText)
        catch ValueError:
          # I just parsed you, this is crazy,
          # here's an exception, throw it maybe?
    

where this gets particularly irritating is when you start writing code like
this:

    
    
        try:
          doSomething()
        catch ValueError:
          pass
    

I nearly always end up writing wrapper functions around that crap.

Java is worse for this because some libraries (including standard ones) abuse
checked exceptions for this. I actually prefer:

    
    
        if f, err := strconv.ParseFloat(text); err != nil {
          // do something
        }
    

or even:

    
    
        f, _ := strconv.ParseFloat(text);
    

for this kind of scenario.

For the truly bad--typically fatal--error conditions and cleanup, IMHO
defer/panic actually works quite well. I certainly prefer this:

    
    
        f := File.Open('foo')
        defer f.Close()
        // do stuff
    

to:

    
    
        try:
          f = open('foo')
          # do stuff
        finally:
          if f:
            f.close()
    

as Go puts the two relevant things together.

Don't get me wrong: I like Python too but I do think Go has a lot going for it
and has a bright future.

~~~
flyinRyan
>try: >> f = float(someText) >> catch ValueError: >> # I just parsed you, this
is crazy, >> # here's an exception, throw it maybe?

No, you have it exactly backwards. It's with _return values_ that you have to
check after every call to be safe. With exceptions you can truly say: if I
don't know what to do if this fails then I don't do anything. You can let the
exception bubble up higher, all the way up to crashing the program if you like
(which gives a nice stack trace that can then be debugged).

With return values, if you don't check then you could be building up more and
more trash and you won't even know it until you either hit a point of code
that finally does look at what's returned, or if an exception occurs (e.g.
segfault).

~~~
Evbn
"Not checking" is just shorthand for pythons " except: pass".

~~~
prodigal_erik
But not checking is invisible. I can't grep for it. It doesn't stand out in a
code review. It's almost never what I meant to do, so it shouldn't have been
the default or easily overlooked, much less both.

------
crazygringo
If you use a language that uses error codes, then you're forced to explicitly
think about what to do in every single error case (or not, and accept the
consequences). This can add a lot of mental overhead, but can result in a much
more robust and well-thought-out program. But because more logic is involved
period (to handle the robustness), the program is necessarily more complex.

If you use exception handling, then suddenly catching errors becomes much
easier, and writing code is faster, but it's not like your program is any more
robust. If you catch an error up at the top, your program or function is
probably just going to quit. Of course, you can add more code to deal
individually with each of the exceptions in more robust ways, even to the
point of wrapping each statement in its own try/catch block, but then it's no
different in practice from returning error codes.

In the end, I just don't see much of a practical difference. In both cases, if
you want to be a lazy or rapid programmer, errors will lead to your software
just not working. And if you want to be conscientious about your errors, then
you can do that in both cases.

Exception handling lets you handle errors "at a distance". It's convenient,
but harder to keep track of. Returning errors keeps your code execution where
you can see it, and you always know where it is. It's simpler, but more
verbose.

~~~
belorn
I would guess that most (99%) of C programmers are lazy or has an rapid
programming style then. Its a rare thing to see C source code that even do the
simple thing like wrapping every write/read/print call with a loop that
detects eintr. In my years of programming, I have yet to stumble on a other
programer who even knows that one should be doing this.

Looking at c libraries and their example code, almost every time, the example
lacks error handling. Its almost like people avoid doing error checking in C
code because of the cumbersome amount needed to be done in order to check each
and every time. If an example code would include error checking, the number of
lines can easy increase 3-4 times the original size.

~~~
kisielk
In Go if you use the return value of a function you must also assign the error
to something, either a variable or explicitly ignore it by assigning it to _.
It was designed this way specifically to avoid the problem of not checking
error conditions in C code.

~~~
osi
This breaks down when the method you call has no return value other than an
error. In that case it is easy to forget.

------
ChuckMcM
Heh, I thought the answer was "Because my blog is named uberpython and it
would be way confusing." :-)

But error handling is one of those topics that really gets people going. From
ABEND in the old IBM batch days, to uncatchable signals like SEGV in unix and
uncaught exceptions in C++ or Java.

I tend to come down on the "decide what you are going to do in the code, right
where the error occurs" flavor of the argument. So checking for errors when
they occur is important to me, what to do next gets to be sticky.

So there are three things you want to do:

1) Easy - you just want to quit/die/exit pretend you never existed. This is
pretty easy and most OS'es and embedded toolkits have something along the
lines of 'exit' or 'exit_with_dump.' So that later you can try to reconstruct
what happened.

2) Is "this is bad, but it might be ok later" where you want to unwind to the
point where this started but not completely exit. Exceptions are pretty good
for this if used well since you can catch them at the 'unwind' point and if
you can attach the unwinding actions (closing files, freeing memory, etc) to
the return stack then it can be managable.

3) You want to plod along in a degraded mode, which means you need some way of
communicating with the rest of your code that you're damaged goods at some
level.

Go has an interesting mix which I haven't used extensively but I wouldn't
dismiss it out of hand. The folks writing it run services that continue
through partial outages so if it were too egregious folks would rebel inside
of Google.

~~~
sitharus
The problem I found is #1 is hard. In C# if I want to just exit/return an HTTP
error I ignore the exception and it happens. In Go for the majority of cases I
have to check the error and call a panic function. I wrote a helper function
to do this - it takes one parameter and panics if not nil.

2 and 3 I've found to be at little less verbose as there's no try {...}
catch{...} around it.

It seems a nice way of avoiding the stack sentinels or setjmp/longjmp that C++
has, which means the output is less architecture dependent.

Personally I don't think Go is quite ready for prime time, but it's definitely
promising. I'm doing a couple of projects with it to see how it runs.

~~~
zmj
Have you seen the Error function in the HTTP package?
<http://golang.org/pkg/net/http/#Error>

------
Jabbles
It's not "tempting to ignore errors". You're the programmer, if you want to
ignore it, do so. I don't know why you'd want to ignore the returned value and
possible error on a GET request - given that 1) it can fail for so many
reasons, and 2) the suggestion that you might be "GETTING" something. Ignoring
the error of a print statement is more acceptable since it's unlikely to occur
and there's rarely a lot you can do about it anyway.

Error handling is explicit, like the quote you quoted said it should be. If
you ignore the returns (either explicitly or implicitly) you pay the price in
debugging.

"leaving me confused as to which error happens when" Panics happen when
something very bad happens. You shouldn't be accessing off the end of an
array, it signals that you've probably programmed it incorrectly, rather than
it being a problem with your input (~ish). Same with divide-by-zero and type
assertions. However, they provide another control path that in a limited
number of situations, can give elegant results.

~~~
d0m
That's his whole point. He _doesn't_ want to ignore errors but feels like Go
is so verbose about it that it's a pain for programmers to deal with it.
Example in case, _print_. Would you check its return value every time you call
it? No.

Contrast that to other languages, such as python, where you don't _ignore_ the
errors because _you know_ that if something goes wrong, an exception will be
raised.

It lets you do stuff like:

def whatever(request): my_int = int(request.POST['some-param'])

Note that, in that case, we don't check if 'some-param' is in POST. Neither
what happens if it's not a number.. Basically, an exception will be raised and
a 404 will be shown.

~~~
0xABADC0DA
It's actually worse than that. The blog has error handling code like:

    
    
        if err := datastore.Get(c, key, record); err != nil {
            return &appError{err, "Record not found", 404}
        }
    

This is pretty typical Google Go error handling, where errors are only
actually used as boolean flags. Errors are universally returned as type
'error' so to actually do anything with the error you have to first cast it to
some specific type. But that type isn't part of the method signature so you
have to dig around in the source or, if you are very lucky, it was documented
someplace. You end up having actual error handling plus even more error
handling for if you got the error type wrong.

For instance if datastore could return 'record not found' or 'io error' then
they would usually be treated the same way by the code because to do otherwise
would be even that more of a hassle.

~~~
agentS
I guess you are super lucky:
[https://developers.google.com/appengine/docs/go/datastore/re...](https://developers.google.com/appengine/docs/go/datastore/reference#variables)
or even
[https://developers.google.com/appengine/docs/go/datastore/re...](https://developers.google.com/appengine/docs/go/datastore/reference#Get)

    
    
      err := datastore.Get(c, key, record)
      if err == ErrNoSuchEntity {
          // entity not found
          return
      } else if err != nil {
          // some other error
          return
      }
    

Don't really see the hassle you are referring to.

~~~
0xABADC0DA
I think this is exactly the point. You have to read the comment to get the
error types (isn't compiler accessible), it doesn't list what all the possible
error types are (doesn't say the ones documented are exhaustive), and the code
returns a singleton error (no state) presumably so the caller doesn't have to
do a bunch of casts..

~~~
agentS
> You have to read the comment to get the error types (isn't compiler
> accessible), it doesn't list what all the possible error types are (doesn't
> say the ones documented are exhaustive)

You're absolutely right. People should be able to use libraries without
reading their documentation, and instead rely on compile failures to slowly
iterate their code towards perfection. /s

Seriously, though, I believe expecting programmers to read documentation of a
library they're using is a pretty low bar.

Also, no it doesn't list what all the possible error types are. To do this
would require what amounts to checked exceptions in Java. The issues with
these are relatively well known.

First, it complicates versioning as adding a new error type is a breaking
change for all clients. When they get an error from an API, most clients
either return that error verbatim, decorate that error slightly and return the
decorated error, or handle a few specific error situations and return an error
in all other cases. Declaring all possible error types makes your programs
brittle, as it is easy for libraries you are using to break your code.

Second, they are a hassle for larger programs that touch many systems. It is
easy to declare that you return an EntityNotFound error. But it is not so easy
to declare that you return an EntityNotFound, MemcacheCASConflict,
FileNotFound, FileExists, PermissionDenied, Timeout, InvalidJSON,
ConnectionClosed, or TemplateRenderingFailed error. This is perfectly
reasonable set of errors for a simple method that gets a value from datastore
(possible caching it) decoding a field as json, and writing a template to an
HTTP connection. Any 'simple' wrapper of this method then inherits all of
these declarations. Now certainly with Java, IDEs will "fill in all the forms"
for you, so this problem is a little more palatable, but Go does not require
programmers to use (often heavyweight) tools to make them productive.

> code returns a singleton error (no state) presumably so the caller doesn't
> have to do a bunch of casts

This is a little disingenuous, as you don't really know why they made it a
singleton error. I can't think of any state that would be useful in this
situation, can you?

The Java version of this API throws an exception with a single field, the Key
that could not be found. I think this is supremely unhelpful and just adds
clutter to the documentation, as the user of this method clearly already has
this information in hand.

------
zik
I suppose it's all a matter of taste. The error handling design decision is
one thing I really like about Go. I write high reliability embedded software
and "throw/catch" just doesn't cut it for me since it obscures possible error
conditions thrown from subroutines. I want to have to explicitly deal with
those errors whenever they might occur and error returns are a good way of
doing that.

For every language feature someone likes there's someone else who hates it,
and vice-versa.

~~~
je42
I cant follow. Throw/Catch is as powerful as a error return value but it does
not let you ignore errors without writing any code.

~~~
wonderzombie
Unless they're unchecked exceptions, of course. In Python, all exceptions are
unchecked.

The proliferation of checked exceptions all throughout the code is a whole
other ball of wax.

~~~
je42
Checked exception need to be handled properly. It depends on the sanity of the
API. However, the standard way how to handle checked exception is a lot easier
to maintain than error return values.

~~~
luriel
The only mainstream language that has checked exceptions is Java, and it is
almost universally agreed, even within much of the Java programming community,
that checked exceptions cause more problems than they solve.

Of course, unchecked exceptions have the problem that is basically impossible
to keep track of what exceptions a function might throw (because it has to
document all the exceptions that any functions it calls might throw too, and
so on and on.)

In the end, of all error handling mechanisms around, Go is by far one of the
best.

------
elimisteve
I write Python and Go code every week, and have since I started using Go
almost 2 years ago.

I appreciate Go because I'm working hard to become an engineer who builds
robust software rather than a sloppy hacker that throws scripts together. One
significant difference between the former and the latter is carefully handling
errors versus not.

Python's error handling seems much more succinct only because most of us don't
both handling errors at all! Every other line throws many exceptions, but we
ignore this for convenience. (Those ugly "except ___:" statements ruin our oh-
so-cool one-liners!)

Bottom line:

When I feel like having fun making something simple and getting it done
_fast_, I use Python.

When I feel like building something that _needs_ to work -- especially
anything that does more than one thing at a time, or should use all cores
efficiently -- I use Go. And yes, that means taking error handling seriously.

------
bproctor
I don't understand why anyone would "leave" a language that was working well
for them just because of some hype. Don't get me wrong, I think we should all
be striving to learn new things, but that doesn't mean we should just drop
everything to jump on the next bandwagon.

~~~
mquander
Your statement seems self-contradictory. Should we strive to learn new things,
or should we not jump on the next bandwagon? Are you saying that we should try
new things but not really try them very hard, or what?

There is such a thing as progress, and in my opinion the way you get it is by
spending lots of time trying new things and seeing which ones work. So I'm in
favor of "leaving a language that was working well for them just because of
some hype."

~~~
zobzu
Because hype != progress. Hype is basically a bag full of air.

You can _try_ new things, but abandon the other ones because of _hype_ (or
thinking it's better cuz "ppl who don't know anything about languages said
so") instead of true technical merit is pretty fucked up :)

Now then again Go ain't bad, but I don't see it being _better_ enough than
Python to switch in this case. Python in better for many things as well. Not a
large step enough.

~~~
mquander
Hype is a bunch of people claiming something is good. If there are enough
people, you should probably go find out whether it really is good. If it
really is, then _voila_! Progress happened. If it's not, you stop. This guy is
busy finding out, that's all.

~~~
zobzu
Fair enough, but I fear that most of the time hype comes from people who fear
missing the latest boat, or who think it'll help them if they say stuff like
XX is cool (a misplaced positive attitude always helps more, than misplaced
criticism)

With mass globalization of the hype, we have countless examples of hype - even
for programming - around things that aren't especially good.

Marketing people know that pretty well.

------
barrkel
Exceptions are like garbage collection.

Garbage collection relieves you from the drudgery, accounting and bureaucracy
of manual memory management and indirectly leads to more expressive forms of
programming once function boundaries are freed from having to specify who owns
the data being passed back and forth. But you still need to be aware of space
vs time usage, you still need to know where memory is being allocated, used
and kept alive in your program, and you can still have "leaks" where a data
structure is rooting too much dead data. This isn't immediately obvious to the
novice, but that isn't IMHO a good reason to dislike the "magic" behaviour of
GC. In the hands of someone who knows what's going on, programs get much
simpler.

Exceptions relieve you from the drudgery, accounting and bureaucracy of manual
error passing and recovery, and indirectly leads to more expressive forms of
programming once function boundaries are freed from having to specify how rich
error conditions are passed back along the chain of callers. But you still
need to be aware of errors that require aborting and errors that can be fixed
and resumed. You can still ignore exceptions that leads to programs crashing
when they shouldn't. Looking at a program using exception handling, it
sometimes isn't immediately obvious to the novice how the exception plumbing
is working (especially since you don't usually see it at the function call
level), but that isn't IMHO a good reason to dislike the "magic" behaviour of
exceptions. In the hands of someone who knows what's going on, programs get
much simpler.

~~~
ryanmolden
>In the hands of someone who knows what's going on, programs get much simpler

I never really got this argument. With RAII types that have value semantics
(ala shared_ptr) what is so difficult about memory management in C++? I guess
there are reference cycles, but weak_ptr can help there. Manual ref-counting
(like a COM AddRef/Release pattern) can be tricky, but that is where attention
to detail, code reviews and basic competence come in. RAII wrappers with value
semantics help here too. Most problems I have seen with that have been people
simply not bothering to learn even the basics or using "smart" objects without
understanding how they work or how to use them properly, which really isn't
that hard, honestly.

>that isn't IMHO a good reason to dislike the "magic" behaviour of exceptions.

Agreed, my primary pain point around exceptions is people that write functions
that mutate some state and are interrupted in the middle via an exception and
stack unwind leaves objects in some franken-state. Transactional semantics in
the face of exceptions don't just happen except in trivial cases (i.e.
functions that don't mutate state or have trivial unwind semantics where they
must only restore say a single mutated value).

That said the same problem exists anytime there is state mutation and
multiple, failure exit paths. For some reason I guess the code I have seen
using error code return style tends to handle this better. Could just be some
kind of selection bias.

~~~
barrkel
In my experience of web server apps, it's been fairly easy to guarantee that
either the whole request succeeds, or - if there was an exception - every
change is rolled back. Both user state and database state has been
transactional, and state that lived on in memory in between requests was
either read-only or caches.

In UI apps, it's much much harder to guarantee transactional semantics unless
you're using persistent data structures or similar techniques that make in-
memory transactions / undo trivial to make correct. So it makes more sense for
exceptions there to save user data where possible, log the error (potentially
back to the vendor), and restart the app.

(I don't want to talk about C++. I think both C++ exception handling and
memory allocation are broken by C++'s design. You can only make it sort of
work with coding standards, and even then it's labour intensive. If you don't
yet understand why GC increases productivity, it's an epiphany you'll need to
look forward to. Bonus: GC also makes exception safety far easier. For
example, you can write a stack.pop() that returns the value popped - one of my
favourite examples of C++ being broken.)

~~~
ryanmolden
>If you don't yet understand why GC increases productivity, it's an epiphany
you'll need to look forward to.

Oh I fully understand/agree with that, I just don't find C++ memory management
to be too hard. The concurrency concerns pointed out by others are true,
though. Whether they (the concerns) are offset by the lack of deterministic
destruction and the extreme triviality of creating reference chains that root
things far longer than needed due to the root being live while the rest of the
chain should be dead, is another question.

As you pointed out, in some domains transactional code is fairly
straightforward, or at least tractable without inducing massive depression. In
some other domains, not so much :)

~~~
barrkel
The domain of programs for which C++ memory management is not hard has only a
partial intersection with programs that are small, expressive and easy to
understand.

That is, if you take C++'s techniques for memory management for granted,
simpler approaches to problems won't even occur to you.

------
rachelbythebay
Rejecting something because it's "from the 1970s" seems like a particularly
weak way to roll. Automatically equating "old" with "broken" is just as lazy
as automatically equating "new" with "pointless".

The world is far more nuanced than that.

~~~
sp332
"Old" as in, the last time this seemed like a good idea was the 70's.

------
cyberroadie
The author of the blog post is overlooking a major feature here. Go does
multiple return types, so you can do things like this: i, err := getValue()
however if you want to ignore a return variable or in this case the returned
error you use an underscore like this (same as in haskell): i, _ := getValue()

This is Go's mechanism for handling (or ignoring errors) which imho is really
nice because you don't have to learn anything extra, like throwing/catching
error mechanisms

~~~
je42
That's not the same as handling an error in a central spot automatically.

~~~
wonderzombie
Handling errors in one spot doesn't work for all situations, though, right?
Especially in an OO environment an object may need context for retrying a
request, or some other kind of error decision making. So in practice some
exceptions percolate through the object graph, while others are handled in-
house, so to speak.

~~~
je42
It works for most situations. If you need retry logic - exceptions is the
wrong way to go. Errors returned by the standard library are most likely not
errors you want to retry by default.

~~~
wonderzombie
That depends, though, doesn't it? Just about anything network-related is
subject to flakiness, disconnection, and timeouts. That means HTTP, database
connections, sockets, etc.

------
james4k
> Verbose and repetitive error handling

If you were to handle the same error cases in Python that you gave examples
for in Go, it would probably be even more verbose with the try/catch wrapped
around.

> Errors passing silently – ticking time bombs to go

Are you kidding? Unless exceptions are documented well, which they rarely are,
this is a much greater problem when using exceptions.

~~~
je42
> If you were to handle the same error cases in Python that you gave examples
> for in Go, it would probably be even more verbose with the try/catch wrapped
> around. Not sure what you are talking about:

try:

    
    
      datastore.Get(c, key, record); 
    
      viewTemplate.Execute(w, record); 
    

catch e:

    
    
      appError(e);

~~~
wonderzombie
Is catching all exceptions really what you wanted to do here? A generic
appError() is not equivalent to handling each possible failure more by type,
as the Go example does.

~~~
je42
My bad. Good point. I looked at what he meant - not what he wrote. :)

------
tikhonj
As usual in these threads about Go, I really wish people would consider
Haskell as a nice alternative. A lot of people write Haskell off as "academic"
or "impractical", which I feel is not an entirely fair assessment.

Particularly: Haskell is fast, concurrent by design (whatever that means, I'm
sure Haskell is :P), typed but not cumbersome or ugly (less cumbersome and
ugly than Go's types, even) and--most importantly for this article--does error
handling really well.

In a certain sense, Haskell's main method for handling errors is actually
similar to Go's. It returns a type corresponding to _either_ an error or a
value (this type, naturally, is called Either). This method is special, oddly
enough, because it _isn't_ special: Either is just a normal Haskell type so
the error checking isn't baked into the language.

Coming from another language, you would expect to have to check your return
value each time. That is, you fear your code would look like this pseudocode:

    
    
        if isError val1 
          then pass val1
          else val2 <- someFunction val1
               if isError val2
               ...
    

However, this is not the case! The people implementing Either noticed that
this was a very common case: _most_ of the time, you want to propagate an
error value outwards and only do any work on a valid value. So you can
actually just write code like this:

    
    
        do val1 <- someValue
           val2 <- someFunction val1
           val3 <- someFunction val2
           someFunction val3
    

then, if _any_ of the values return an error, it gets propagated to the end of
the code. Then, when you want to check if you actually got a valid value--
maybe in some central location in your code, or wherever is convenient--you
can just use a normal case analysis.

Additionally, while the errors _do_ pass through essentially silently, the
type checker does ensure you deal with them at some point before using or
returning them. If you ever want to get the Int from a Either Error Int, you
have to either handle the error case somehow or explicitly ignore it (e.g.
with a partial pattern match). The latter option will generate a compiler
warning, so you can't do it by accident without being notified.

So the mechanism is simple, but it can also stay out of your way
syntactically. So what else is this good for? Well, it's just a normal data
type, nothing special; you can use existing library functions with these
values. For example, you can use the alternation operator <|> to find the
first non-error value:

    
    
        val1 <|> val2 <|> someFunction 5 <|> ...
    

This is often a very useful idiom which would be harder to write with a
different way of handling errors. There are more utility functions like this
(e.g. optional) that let you make your intents very clear at a high level. The
optional function, for example, lets you do exactly what it claims: you can
mark a value as "optional", meaning that any error from it will be ignored
rather than propagated.

You can also layer on this error-handling logic on other similar effects. For
example, there are some types (like LogicT) that represent backtracking
search. Combining error-handling with nondeterminism gives you a question:
should an error cause the _whole_ computation to fail, or only that particular
branch? The beauty is that you can choose _either_ option: if you wrap LogicT
with ErrorT (this is just like Either except it can be combined with other
types) an error will cause the _whole_ computation to fail; if you wrap ErrorT
with LogicT, the error will only cause the current branch to fail. This not
only makes it easy to choose one or the other, but also makes it very clear
which one you _did_ choose: it's right there in the type system in a very
declarative fashion.

Haskell also has a bunch of other advantages which aren't worth going into
here. I think anybody looking for something like Go--a fast, high-level,
concise, typed alternative to Python--should _definitely_ consider Haskell.
While it may not seem so at first, I believe that Go and Haskell are good for
a very similar set of problems and so actually overlap quite a bit.

If you really dislike some particular things about Haskell, you should also
consider some similar languages like OCaml and F#. I personally prefer
Haskell, but there are definitely good cases to be made for either of the
other two.

~~~
cletus
This kind of comment kinda misses the point.

Haskell (IMHO) won't ever be mainstream for much the same reasons Lisp never
was (or will be?): it has an incredibly high learning curve (eg [1]).

This is really the problem of the "pure" functional languages. Functional
programming is suited to some tasks. With others the fit is almost tortuous.
Imperative programming is well-suited to how we think and how we break down
tasks.

If you look at the popular languages they're pretty much all multi-paradigm
(eg Ruby, Python, Go, C#, arguably even C++/Java) meaning they give you the
low-hanging fruit of functional programming while still being in a relative
sweet spot of being easy to learn yet reasonably expressive.

Why I think Go has a very bright future ahead of it is that it is the first of
these multi-paradigm language to combine all these features:

\- easy to learn (seriously, go look at how short [2] is; you can knock that
out in an afternoon);

\- it is minimal. I LOVE Go's minimal OO model for example; and

\- it is statically typed.

If you look at the other statically typed multi-paradigm languages you have
C#, which is generally well-regarded... except (Mono notwithstanding) it is
very Windows-centric. Java is, well, Java. C++ is _incredibly_ complicated.

The only thing Go is missing is a mode for (semi-)manual memory management.
I'm thinking something like Obj-C's ARC (in iOS 5+) and it could well supplant
C/C++ for the vast majority of their (already shriking) use cases...
eventually (Go has some work to do on speed).

[1]: [http://stackoverflow.com/questions/377082/how-long-does-
it-t...](http://stackoverflow.com/questions/377082/how-long-does-it-take-for-
you-to-be-comfortable-with-haskell)

[2]: <http://www.miek.nl/projects/learninggo/>

~~~
theaeolist
This kind of comment kind of misses the point. The monadic style of threading
error values is perfectly compatible with imperative programming if only
language designers knew about it.

~~~
nostrademons
Problem is that you then have to explain monads to average programmers who'll
be using the language.

I love monads (I love arrows more, but that's another issue). I'm a language
design geek. My level of expertise is different from someone who has just been
hired into a new job and wants to get things done.

I suspect that most language designers know about monads - I'm not sure Rob
Pike did, but it's pretty common knowledge among folks who do this stuff.
Until you can explain them to the folks who'll be using the language in a way
that doesn't make them seem like arcane black magic, though, you'll find
programmers will just say "I don't get this" and use what they're familiar
with.

~~~
flyinRyan
>Problem is that you then have to explain monads to average programmers who'll
be using the language.

No you don't, this is exactly what you don't need to do. Do we explain the
finer points of stream implementation to would-be C++ programmers? No, when
they need to do something non-standard with a stream/monad, that's the time to
talk about them. For the majority of programmers it's just "when you need to
write to a file you do it like this".

~~~
morphyn
The Either type described in the first comment is also a Monad. They are more
important in Haskell than just "when you need to write to a file..." and I
don't think you can totally grasp the error handling method without
understanding them.

~~~
flyinRyan
I couldn't disagree more. The Either type may have a Monad interface, but it's
a Sum type and languages that don't have thousands of Monad tutorials also
have Sum types. There's no need to explain Monad theory to someone just to
explain Sum types or even why chaining do expressions doesn't do unnecessary
computations in the face of errors. Just show them the code for join and >>
and they'll see why it works. No need to bring up Monads.

------
mseepgood
Of course there is a difference between an "index out of bounds" error and a
network connection error: one is a programming error and the other is not.

------
yason
On the other hand, us who haved loved and embraced C don't mind this at all.
It's up to us how to arrange the error handling.

Different situations warrant different strategies. Sometimes it's ok to return
NULL, sometimes it's ok to return true/false and pass the actual result back
indirectly, sometimes it's ok to return the value but use an external error
flag. Sometimes it's ok to mix control flow and external flagging of errors,
namely what an exception is, effectively.

However, in my opinion, languages who point too much to one single error
handling mechanism are more irritating than languages that leave it up to the
programmer. I'm particularly wary of exceptions per se: they're a really nice,
clean concept but on the other hand I rarely hit an use case that would be a
perfect hit for exceptions, and even those perfect cases for exception-
handling wouldn't look too shabby if designed with other error handling
strategies.

On the other hand, many cases where errors are handled with exceptions get
awfully ugly in the normal case. For example, the "try: ... except <name-of-
error>: pass" idiom in Python. I can't count the number of times I've written
a small wrapper function around a trivial exception handling case, that just
flattens the result and error into a suitable value if that fits my use case.

------
belorn
When dealing with C and error handling, I have rather simple rule. If in the
process of writing a simple program (+1k lines), and you did not find a bug in
the libc documentation, then you are not looking hard enough at return values.

I do not have the same rule for python.

------
chrismorgan
I see both pros and cons in the Go approach.

I find Python's exceptions model to be something that _does_ occasionally
frustrate me. For example, I'm used to using NSIS for some things (the
PortableApps.com Launcher being one thing); its model is that a command may
set the global error flag. This means that some sorts of things will fail
silently - occasionally bad, but generally good. Some styles of Python scripts
(generally automation scripts rather than full-blown programs) would do much
better ignoring most errors. When you have a script merrily chugging along and
all of a sudden due to some obscure corner case (perhaps not even documented)
in a small, incidental part of the script the _whole thing_ quits, it can be
rather annoying.

The Go approach lets you catch errors if you want, or drop them, much more
easily than Python approach. My inclination at present is that the Go approach
is more likely to end up with error-resilient code, as it encourages dealing
with errors each time, while Python typically encourages you to let the caller
deal with an exception if you can't - but often I think the caller doesn't
realise that he should. Or after a couple of levels of indirection, it's not
even documented that that exception can occur. (This sort of thing, I believe,
is something that Java's checked exceptions were supposed to help with -
either deal with the exception or declare it.)

For myself, I've had lots of experience with Python and am now dabbling with
Go, using it for two smaller projects to decide which to use for my next major
project. At the very least, I think the approach is worthwhile trying in a
serious project that its merits and those of the Python approach may be more
completely analysed.

------
TazeTSchnitzel
I'll agree that this is perhaps the single thing that I don't like about Go.
Now, you can argue it helps with "explicit error handling" and all that, but
in that case you might as well have must-be-caught exceptions.

Anyone want to fork Go?

~~~
nilsbunger
Only if we call it Goto

~~~
nostrademons
Or perhaps GoTwo.

------
agentS
Maybe its just me, but I like the fact that adding exponential backoff to an
HTTP request goes from <http://play.golang.org/p/8WQ8XXrKw0> to
<http://play.golang.org/p/3tCiNhAfo3>, in no small part due to the explicit
error-handling style of Go.

------
noah256
Psh, obviously trigger_doomsday_device will only respond to an http POST.

Once again the president has been saved, thanks to HTTP Pedantry.

~~~
politician
> HTTP Pedantry

I propose a new HTTP verb, MEH, to address the belief that the HTTP verb
mechanism is unnecessary or over specified. The semantics of HTTP MEH are
application-defined, so it can be used to fetch, update, replace, query,
delete, or whatever the application defines to be necessary. Services honoring
HTTP MEH implicitly signal that interoperability is not supported, but that
intermediate proxies 'should do the right thing' regardless.

------
ericbb
On the topic of when to use error-returns vs. when to use panic:

I struggled with this aspect of Go coding too and what I decided was that, in
order to choose your approach to errors, you should think about how important
it is that your function can be composed into an expression:

    
    
        x := foo(a) + bar(b)
    

vs.

    
    
        c, err := foo(a)
        if err != nil {
            ...
        }
        d, err := bar(b)
        if err != nil {
            ...
        }
        x := c + d
    

It's a trade-off. By using "panic", you enable more concise and compositional
code. By using error-returns, you are being more explicit and making it easier
for the caller to handle errors at the call-site.

Usually, functions that can fail are not the kind you desperately want to use
in expressions anyway. So error-returns are more common. But there are many
situations where errors can happen and where composition is a big win. In
those cases, you reach for "panic".

If you look at some of the built-ins that can panic--I'm thinking of array-
indexing and division and regexp.MustCompile(), for example--you notice that
these would become very cumbersome if they used error-returns instead.

There's also the issue of initialization. That's the justification given in
the case of regexp.MustCompile(). An initializer must be a single expression.

The words "panic" and "error" probably lead people to think that you should
make the decision based on severity but I don't think that's right.

(That's just my take on it. I'm no Go expert so there are likely better ways
to think about it.)

~~~
wonderzombie
As I understand it, it is about severity. I think "panic" and "error" are
meant to handle situations where there _is_ no obvious, correct answer, like
with array indexing. A nil pointer is another gimme example. Some languages
have unchecked exceptions — maybe conceptualize it somewhat like that.
panics() are for disasters, for serious program errors.

By contrast, a network timeout is not a disaster. It's not "normal" or desired
behavior but it's well within widely-known modes of behavior.

Composition is nice-to-have but in general you ought to be able to write Go as
if everything that does _not_ return errors will succeed under the vast
majority of circumstances, or where failure is inevitable.

Recover is, among other things, a failsafe of sorts for cases when code you do
not control (e.g. a library) panics. It might be catastrophic failure for the
library, but it may not be for your code, and you need an escape hatch.

...I am not an expert, either, but I've spent a fair amount of time on the Go
mailing lists. I feel like I'm actually repeating something I've read, but
Effective Go doesn't talk much about this. (It does say that library functions
should avoid panic.)

~~~
luriel
Panic() should only be used to signal either programmer error, or truly panic-
worthy situations where state is so messed up that crashing is the only good
option.

Put another way: when you call an API correctly, it should be safe to assume
it will never panic().

Panic/recover can be used in rare occasions within libraries to do more
exception-ish style error handling, but those panics should _never_ be allowed
to escape and cross API boundaries.

------
knodi
This kind of error handling is the best part of Go.

------
pavanky
>That problem is errors are handled in return values. 70′s style.

>This is one of the things I can’t stand in C.

Having to check error values for every function call is indeed a pain. But C
has macros and I think it is a nice and elegant way to handle errors. I
personally prefer MACROS to exceptions when writing C/C++ code. But to each
his own.

Does anyone know if go supports macros ? If they do not, it is one ugly
problem to have!

 __EDIT __I just realized you can write custom exceptions that can provide
similar information about line numbers, functions etc. So removed a line
saying that was a plus for macros.

------
dvhh
I am eternally asking myself how do people handle exception in multi-threading
programs ? Not that error code solve the issue at all, but it look easier
(less complex) to handle thread error with error code

~~~
luriel
Well, given how hopeless it is to try to do threaded programming with Python,
I guess that is not a consideration Python programmers usually worry about.

------
jorgem
Out of curiousity: Aside from "returning errors" and "throwing exceptions", is
there any interesting research into other ways of error handling?

~~~
ufo
Common Lisp has an interesting exception system where errors have a chance to
be handled without unwindind the stack. Basically, functions also receive an
"error handling" object as an extra argument and they consult when they
encounter an exception. The error handling object then edcides wether to
continue operation, or to unwind back.

~~~
masklinn
> Common Lisp has an interesting exception system

Called "conditions", fwiw. Smalltalk has a very similar system (the main
difference is in the declaration of restarts)

------
jonmrodriguez
Someone can fix this by creating and popularizing a simple preprocessor that
implements a macro called "safely" that expands to something like "call this
function, and then if it errors, report the error to STDERR and abort
execution". Then just prefix the majority of your commands with "safely", and
implement your own error-handling in the few cases where you care.

------
Tichy
What happens in Go if there is an exception (division by zero or whatnot)?
Does it just exit, no stack trace? I am so used to exceptions that I don't
even know how it used to work without them. I think getting a stack trace for
debugging is really important...

~~~
rogpeppe1
You get a panic, which can be caught. <http://play.golang.org/p/IaTAGizhrF>

------
hiphopopotamus
Personally I think the community might be spending too much time on Python 3.

Python 2 is fine, and the upgrade will offer few significant advantages for
most people.

I think the effort would be better spent working on things like frameworks and
tools for web development.

------
exabrial
My opinion: I'm not a fan of Python.

* It's really slow. * Most people write their code statically, but it's dynamically typed language anyway. * Things like '__init__'... what? * Having something you can see (whitespace) be significant is a bad idea. * Even worse, the community likes to use spaces instead of tabs, which makes the 'significant whitespace' a significant problem. * With the exception of Flask, important design patterns and lessons: DRY, SOC, DI, SRP, etc are largely ignored and called "Java-like."

Anyway, that's my $0.02

~~~
lmm
Sure, python is slow. For problems where this matters, there are usually
python libraries available that will do it much faster than pure python (e.g.
for scientific number crunching you use numpy, which is using FORTRAN behind
the scenes). There are probably some problems where performance is critical
and no libraries are available, but not many.

Sure, python is dynamic; at the time it was first created it was hard to
provide the level of flexible ("duck") typing python has in a static
environment. Now that typing has advanced, we are starting to see some level
of type annotation support in python; I would expect it to get static typing
eventually, but we can't break existing code. Still, I can totally understand
leaving python for a static language with good type inference, like haskell or
scala. I don't think go's type system is powerful or elegant enough though.

__init__ is fantastic; it removes all the special cases around constructors
you see in other languages, and makes it "just another method" that follows
all the usual method resolution rules etc. This means less to learn, less to
bite you, and much easier to e.g. refactor a constructor into a factory method
(or vice versa).

Whitespace isn't significant, indentation is. And when you look at a piece of
code, you can see the indentation far more quickly and easily than you can see
the braces. At least I can.

DRY, SOC, SRP are very much part of the python philosophy. I find python's
standard library is much better at following "tell, don't ask" than most
languages'. I agree that many python programs should follow the DI principle
better and possibly the language should have better support for it, but IME
the java/spring-approach as it's usually implemented doesn't really provide
DI, it's just a complex reimplementation of singletons. I've yet to see a
language or framework that can really help programmers do DI right if they
don't already know.

------
dschiptsov
The principle behind Go's design decision is based on actual experience and
need to be understood by amateurs.

The idea is quite simple: _errors are not some rare special cases, they are
ordinary, general events_.

That means there are no need to some special mechanism for handling them. They
should be handled as ordinary events within common FSM.

There is no contradiction in using the general mechanism of examining returned
values to determine the state of procedure execution.

Any FSM requires _if_ (or _cond_ ) statements. It is the essence of a
program's logic.

The mechanism of exceptions was wrong. It is wrong in Java, it is wrong in
C++. It is just a bad design.

The good design is using the same mechanism the underlying OS uses. In case of
UNIX-derivatives it is the explicit checking of a return value. Because errors
are common and ordinary events.

Amateurs believe that errors are rare, and they will avoid them. This is over-
optimistic - in practice errors are of nothing special. It is just an
alternative branch of a condition, where a consequent assumed to be a success.

------
hasenj
The error handling style is certainly different - but it's not so bad. It's a
matter of style and getting used to stuff.

Maybe a better error handling mechanism is needed - but I'd prefer if it's not
exception handling. Perhaps lisp style error handling? What ever that is - for
I'm not familiar with it - but I keep hearing lispers brag about how awesome
it is!

~~~
mquander
[http://www.gigamonkeys.com/book/beyond-exception-handling-
co...](http://www.gigamonkeys.com/book/beyond-exception-handling-conditions-
and-restarts.html)

Think exceptions, but instead of blowing away the stack on the way up to your
exception handler, it gives you the option of resuming execution in some way
where you left off. It also lets you encapsulate error handling better.

~~~
smithzvk
I am glad to see some CL condition system pop up here. This article basically
turned me off from Go and reading peoples support for C style error handling
has made me question what the heck they are thinking.

There are actually several nice things about the CL system, but nothing there
is going to convince someone that actually thinks not only that having every
return value in your language serve the role of an error signal is reasonable,
but actually arguing that it is equivalent from a programmers perspective or
even a better method of programming.

~~~
dchest
Instead of insulting people and declaring that you can't convince them, try
posting some useful information about CL condition system and why you think
it's better than everything else.

~~~
smithzvk
First let me apologize for the length, second I will say that to my knowledge
none of this is about the CL condition system in particular, third this is
basically a brain dump because I need to get back to work and still don't
understand the error code camps points...

I definitely wasn't trying to insult but I recognize that I wasn't being
helpful either. I was basically throwing up my hands. And while I like CL's
system, I haven't come close to trying everything out there so I can't be a
judge; I mean this not like a cop out but seriously, I am very poorly informed
about what other languages have to offer.

But since you ask: I guess if I was trying to convince someone that exceptions
are inherently a better mechanism, I would start by exploring how arithmetic
is handled in the C language. Let's say we have a function called "add" that
adds two numbers. Typically we would think expect that the return value of
such a function should hold the result of that addition. It would be naive,
however, to expect add to always complete without something like an error
happening. While addition of two numbers seems safe, it isn't when we start
using our imperfect number representations such as floating point numbers
(susceptible to overflow, underflow, loss of precision, and more) but also
with machine sized integers (susceptible to overflow) and even bigints (what
happens if we attempt to add two numbers and exhaust system heap space?). The
way these are currently handled in the C language (to the best of my
knowledge) is by three different mechanisms: 1) floats return special floats
that say indicate that there was an error somewhere, like NaN (though you can
probably instruct your OS/hardware/whatever to issue these as signals instead
of silently spitting out an NaN), 2) wrap-around is loosely taken as a
standard but truly it has undefined consequences, your code must guarantee
that this cannot happen, and 3) the program will probably exit. This is messy
but we have learned to deal with it and has become second nature to C
programmers; but that doesn't means it is good.

But this doesn't need to be the case, we could use the return value checking
that is being promulgated by some here. We can define add as...

error_code add(int a, int b, int *return)

...where int could be any number type and translate every occurrence of "a+b"
in our code into something like...

int ret; if (err = add(a,b,&ret)) { // err handling here } // ret holds the
answer here

I cannot believe this is preferable to anybody, in fact I am going to go out
on a limb and say that it isn't. Instead we just trust this to work when we
know that there are instances where it won't and, in the case of floating
point math, it is extremely common for you to hit those cases where it doesn't
work. If we were serious about writing code that handled these errors
gracefully, our C code would be unmaintainable. If we were honest about the
source of this laziness, I think we would say that this is due to the
syntactic and mental overhead of the error handling. The fact that we don't
code like this, even the very fact that C will spit out an NaN and propagate
it along indefinitely, is proof in point that people dislike this type of
"check the return value" error handling. This is C more or less pushing us to
write code that is less robust.

If we really wanted to consider what happens in a Common Lisp system, you
would have a function "add" that takes two numbers and will always return the
correct result of that addition or won't return at all. This means no wrap
around overflow, no NaNs; this is vastly cleaner. However, as far as I know,
this has nothing to do with CL in particular, this is presumably how Python or
any language with a exceptions works. This is what I cannot fathom: that there
are people that are willing to throw away this simplifying assumption because
it means that your code might have controlled non-local transfer of execution.
This is what Joel on Software directly says (linked elsewhere in this thread);
exception handling is like goto, goto is bad, even when it is contained within
an apparatus fixes its problems, like the exception handling apparatus. Or
that people think the same amount of work is involved in either system? How
does that make sense? Which way would you rather treat something like "add(a,
add(b, add(c, d)))" because to my eye the C style handling adds a bunch of
boiler plate code that I'd rather not write while adding little to no value.
Like I was saying, I cannot fathom it so it is hard to form an argument
against the alternative stance that I do not understand. Perhaps there are
places where we want our execution so close to the structure of the code that
handling errors C style makes sense, but I have to imagine that they are few
and far between.

If we compare a more common example where people will place error handlers:
writing to a file. Is it better to have writefile(file, data) return an int
that indicates error, or is it better to just say "writefile puts this data in
file, period". Here are two errors that might come up when writing to a file,
we don't have write permission for that file or the disk is full. Where do you
want to handle these errors? One error happens when we open it, the other
happens when we attempt to write to it further down the call tree, but
presumably we might want to catch both and deal with them at the same
location. With exceptions you catch/handle the exceptions you are interested
in. When returning error codes you have to manually defer the error up the
call tree, affecting the interface of every calling function up to the highest
function that can actually handle the error. So you can add the boiler plate
code to combine and propagate errors up the call tree to your task list now.
At the root of this is the exact same issue as the previous point, it adds
syntactic and mental overhead for the programmer.

This article seems to have spawned some responses. Consider this post:
[http://www.yosefk.com/blog/error-codes-vs-exceptions-
critica...](http://www.yosefk.com/blog/error-codes-vs-exceptions-critical-
code-vs-typical-code.html) He makes claims like in the code...

open_the_gate() wait_for_our_men_to_come_in() close_the_gate()

...if there is an exception inside wait_for_our_men_to_come_in then we'll
never close the gate. While this is true for the code he's written, most
people would (or should) consider that code buggy. In my experience and the
comments of that post confirm it, most languages have a mechanism to ensure
certain side effects happen even under non-local control transfer. Where are
the points of intermediate state that he render exceptions equally bad as
error codes? Are they the states that we explicitly account for in any bug
free program, like closing the gate if there is an problem bringing the men
in? I will reiterate, I truly am at a loss, this is not a rhetorical question.
It seems like I am missing something.

To sum up: One method seems like a clean method of dealing with errors that
will happen, the other seems like a nightmare syntactically and basically
consists of code that I deeply feel should be up to a compiler to write.

