39 comments

[ 2.6 ms ] story [ 68.8 ms ] thread
From the feedback wiki, it's clear that check/handle wasn't that well received. These requirements might illuminate a viable path.

https://github.com/golang/go/wiki/Go2ErrorHandlingFeedback

The discussions I saw on Reddit were strongly in favor of `check`, and the link you provided seems to show a 50/50 split, so your summary surprises me. People suggesting changes doesn't indicate that people oppose the idea, in my opinion... it means they're interested in making sure the execution is good.
I don't see where the 50/50 split is. I didn't imply that the feedback rejected any change to error handling.

Re 'check', more than half of the counter-proposals suggest various ways to select a named handler, which check cannot do.

> I don't see where the 50/50 split is.

There are 3 articles under "In support" and 3 articles under "Against". A pretty literal 50/50 split.

Ah, indeed :-) But many of the counter-proposals offer very different solutions than check/handle, so I'd count them as variations of "against".
Go's resistance to exceptions looks like Steve Jobs and his one button mouse from here. It's not like renaming them to panic/recover fooled anyone outside of the Pike reality distortion field. Sometimes unwinding the stack is exactly what you want. Optional types are nice too, but that's more for things that are intentionally missing. I have yet to see anything here that's an improvement over just doing the obvious thing.
I'm not sure about that. Rust's use of a Result type instead of exceptions is one of my favourite things about Rust. Before I'd used it, I'd not realised how many reliability issues in other languages were caused by overlooked exception cases.

Exceptions are great when you don't want to handle errors properly, but when you do want robust error handling I find the "return a value" pattern much better. Notably even C++ is looking into implementing this!

They are not overlooked, they are better handled further up the call stack. And not having the option to forward them implicitly is a step back, not forward. There's no way to make a programming language better by forcing users to do anything, in many cases the user will have more experience than the person implementing the language. A programming language is a tool, predicting and dictating how the tool is to be used is missing the point.
I don't think forcing is the right word.. I've noticed that languages where the "right way" is the path of least resistance the code quality stays high even if a lot of people with varying levels of expertise contribute
Well in my experience golang Is a long ways from this anyway ... It's a very prescriptive language
> There's no way to make a programming language better by forcing users to do anything

Sure there is, many important language features boil down to forcing users to do the right thing (not necessarily the easy thing).

In highly concurrent languages like Go, there’s not always a meaningful stack to unwind, and you end up having to pass error values around anyway. So I can easily sympathize with the decision to stick with one mechanism for error handling.

> many important language features boil down to forcing users to do the right thing

this just causes the users to use another language for their projects instead

And fixing the output of that is where the rest of us often profit from.

A language that provides a lot of freedom when it comes to expressiveness without offering much in the way of constraints (to truly convey the purpose of a given piece of code with the least amount of ambiguity possible) will almost definitely turn into utter crap in contact with junior developers, and will often be a source of pain for more experienced ones once the project is nontrivial, whether they realize it or not.

Do you see any problems with optimizing everything we do for junior developers? I mean, most of us aren't, and it would be really nice if someone gave a crap about wasting our lives as well.
It's not necessary optimizing for junior developers. When properly implemented, language hand-holding is a tool for "better" programmers as well. I find that my brain capacity is limited, and the more I can focus it on issues that are productive rather than eg. tracking the liveness status of my data or potential system-wide effects messing with it, the more useful work I can perform.

Not to mention working together with more junior people, in which case it will be you doing the hand-holding instead, because the tools don't

(comment deleted)
Forwarding errors upwards is one character in Rust (?). IMO that is pretty much the perfect balance between forwarding errors easily and explicitness.
> I'm not sure about that. Rust's use of a Result type instead of exceptions is one of my favourite things about Rust.

It works because Rust has pattern matching and generics as well. Just having a Result type isn't going to help with error handling much, and Go is not getting generics Rust style nor pattern matching any time soon.

Let us be reminded that Go errors as values are purely a convention, nothing in the language mandates their use. On the other hand panic/defer/recover are language constructs.

Exactly the same with Scala.

Option types demand pattern matching and similar techniques for working with those types. Otherwise you just have "if (x.isDefined) a else b" blocks of code everywhere.

Exceptions are really no less complex than return values, and they do not (easily) play nice with asynchronous and lambda/functional programming models. Witness their near abandonment in JavaScript for example.

I like Go mostly the way it is. Its simplicity is genius. Anything added should be considered with great care.

I don't think that Go will move to exceptions. Sure, panic/recover are renamed exceptions (very similar to Rust panics, btw), but standard library and community libraries uses errors, so you'll use them anyway (unless you want to rewrite or wrap the world which is possible, I guess, in automated way and that's a provoking thought!).
The standard library uses panic/recover to sort of implement exceptions here and there. It's a pretty serious case of do as I say, not as I do.
(comment deleted)
Exceptions are like cancer. You never know when you're going to get one, you have no idea how to handle them or if you should handle them, and they produce fatal bugs at unexpected places.
They are used for exceptional problems, expecting one doesn't really make any sense.

Of course you do, that's the point; to unwind the stack until you reach code that knows how to handle the error.

Cancer, really; is that you Gordon?

Errors are not exceptional problems.
I didn't say that, I said that exceptions are used for exceptional problems. Expected errors like missing values are better handled using optional types. I find the one ring to rule them all attitude less than constructive.
I'll annonance my bias towards go. But from what I understand, allowing exceptions allow for more complex code parhs. Whereas explicit error handling forces you to handle it in the moment.
Yep yep, sounds good.

Problem is you're not handling errors, you're wasting precious energy shuffling errors to code that knows how to handle it; which is exactly what exceptions could do for you. The end result is the same, except it takes more effort and the code looks like crap.

I scanned through almost all of the posts on the feedback wiki, and this was the only one I saw which addresses handling recoverable errors.

Recoverable errors are definitely a thing, but the original error handling proposal forces the handler chain to eventually return, making it impossible to continue function execution [1]:

> If the enclosing function has result parameters, it is a compile-time error if at the point of any check expression none of the handlers in scope is a terminating statement. Note that the default handler ends in a terminating statement.

As suggested in section H of this post, I think `break` or a similar mechanism inside handlers would be very useful.

[1]: https://go.googlesource.com/proposal/+/master/design/go2draf...

No word about adding proper stack traces by default?

Currently, your best bet of finding where an error was actually generated, if it is returned a few times before finally being logged/returned to an RPC client, is searching for fragments of the error string and hoping that a) the part that you searched for is part of the literal and not dynamically built b) it is unique c) you can guess the call path that led to this error.

Of course, in a perfect world, all error messages would be so descriptive that you could immediately find the issue just by reading the error message, but unfortunately, ideals and ideology rarely create solutions that work well in the real world.

One of the criticisms of exception handling is that it spends cycles assembling a stack trace before it knows that it's needed.

Go generates a stack-trace for panic() or via runtime/debug.PrintStack(); I recommend the latter before log.Fatal().

A nifty code snippet I use for this:

  // ErrLine returns an error formatted to include the line 
  // number of where it occurred (presumably with a performance penalty)
  func ErrLine(err error) error {
    if err != nil {
      // notice that we're using 1, so it will actually log the where
      // the error happened, 0 = this function, we don't want that.
      pc, fn, line, _ := runtime.Caller(1)
      err = fmt.Errorf("[error] in %s[%s:%d]: %s", 
      runtime.FuncForPC(pc).Name(), fn, line, err.Error())

    }

    return err
  }
Edit: I misread this comment. Apologies.
(comment deleted)
yes, why use a function
(comment deleted)