News for hackers. Stuff that matters.
Re:Errors in Go: From denial to acceptance by josteink on Monday November 26, 2018 @01:18PM (#18535527)
I’m sorry but to me this just looks like Stockholm syndrome all the way.

The approach suggested for Go 2 under bargaining is basically a reverse try-catch at best and a Visual Basic “on error goto” at worst.

Even in their own attempt to explain why try catch is bad they effectively admit it serves a common use-case: A common use-case not handled by Go no less.

This lacking also puts the burden on the programmer which will have to manually handle each and every error, by himself, everywhere, one by one.

Even worse are the priorities given in the language: checking for errors is cumbersome, while ignoring errors is easy.

I’m willing to be proven wrong, but my immediate guess is that this in general leads to more buggy code, not less.

Re:Errors in Go: From denial to acceptance by networkimprov on Monday November 26, 2018 @02:44PM (#18536200)
Go 2 is likely to get a per-call variation of try/catch via named handle blocks, see:

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

and this broad survey of error handling requirements:

https://gist.github.com/networkimprov/961c9caa2631ad3b95413f...

Also the author overlooks the Error Values draft design, which is tentatively targeted for Go 1.13:

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

Re:Errors in Go: From denial to acceptance by TheCoelacanth on Tuesday November 27, 2018 @12:37AM (#18539719)
I believe handle blocks is exactly what they were referring to as "basically a reverse try-catch at best and a Visual Basic “on error goto” at worst".
Re:Errors in Go: From denial to acceptance by networkimprov on Tuesday November 27, 2018 @03:45AM (#18540648)
Based on the feedback page linked above, I believe what is likely for Go 2 is quite different than "check/handle". See the requirements page linked above for more.
Re:Errors in Go: From denial to acceptance by sephware on Monday November 26, 2018 @02:18PM (#18536009)
Without taking sides on the issue, these are purposeful trade-offs that the Go team makes. They are trying to avoid bloating the language and making it too difficult for them to add optimizations or other features. And I think they would argue that it makes ignoring errors harder, since you have to intentionally discard the error value when it was given to you, which takes conscious effort, rather than omitting it from the code completely like other languages let you.
Re:Errors in Go: From denial to acceptance by aikah on Monday November 26, 2018 @02:29PM (#18536101)
> these are purposeful trade-offs that the Go team makes.

Trade-offs for whom or with what in mind? the compiler or the programmer?

That's the "philosophical" difference between the Rust team and the Go team.

Re:Errors in Go: From denial to acceptance by TylerE on Monday November 26, 2018 @10:45PM (#18539157)
The cynic in me says it's optimizing to hire lots of low-skill programmers at Google for cheap.
Re:Errors in Go: From denial to acceptance by Skunkleton on Monday November 26, 2018 @03:39PM (#18536597)
Exactly. That is why Rust does so many more things, but Go compiles so fast.

Even with two wildly different approaches to language design, each has their place, and their own set of drawbacks.

Re:Errors in Go: From denial to acceptance by repolfx on Monday November 26, 2018 @06:03PM (#18537661)
It's a false dichotomy. Languages like Java or Kotlin compile fast and have exceptions too.
Re:Errors in Go: From denial to acceptance by mmirate on Monday November 26, 2018 @11:01PM (#18539227)
Because the JVM takes another tradeoff against Go and Rust: it has a runtime interpreter+JIT, which causes even more performance overhead than Go's GC let alone Rust which - at runtime - is effectively a zero-cost abstraction over C and jemalloc.
Re:Errors in Go: From denial to acceptance by repolfx on Tuesday November 27, 2018 @02:24PM (#18545764)
The JVM GCs generally have much lower overhead than Go's does. Go imposes enormous collection costs on apps to try and drive latency so low. This is widely acknowledged in discussions of the various GC tradeoffs.
Re:Errors in Go: From denial to acceptance by mmirate on Tuesday November 27, 2018 @10:34PM (#18549095)
The GCs alone? Yes. The rest of their respective runtime architectures? Highly doubtful. As GC-dependent as Go may be, it still does end up as native code.

But frankly, comparing Go to Java when things like Rust exist ... seems analogous to comparing the z80 to the AVR when 32-bit ARM cores exist.

Re:Errors in Go: From denial to acceptance by repolfx on Friday November 30, 2018 @04:18PM (#18572710)
Offtopic/meta: for some reason your post was dead. The comment at the end is kind of pointless and flamey, Rust isn't that good, but doesn't seem to justify a post kill. I vouched for you to bring it back.
Re:Errors in Go: From denial to acceptance by hactually on Monday November 26, 2018 @07:28PM (#18538170)
Not quite - Java is faster than Kotlin [0] but I've never seen a Golang app take more than 10 seconds (and that's something huge like Kubernetes or Docker components)

[0] https://medium.com/keepsafe-engineering/kotlin-vs-java-compi...

Re:Errors in Go: From denial to acceptance by rhencke on Monday November 26, 2018 @07:51PM (#18538309)
The article you link comes to a different conclusion:

> With the Gradle daemon running and incremental compilation turned on, Kotlin compiles as fast or slightly faster than Java.

Re:Errors in Go: From denial to acceptance by yawboakye on Monday November 26, 2018 @05:27PM (#18537426)
Go optimizes for fast compilation, as such the language has fewer internal checks for only the absolutely necessary. In my opinion, to be able to continue execution when an error happened earlier could be a blessing or a curse, and it depends on the programmer. I've been writing Go for 2 years now and I don't have a single case where I accidentally ignored an error. I have ignored errors, but usually for operations whose end result I don't care about or doesn't affect what happens next in the program.

There's also a recommended way of dealing with errors. I may not state it correctly but basically you build a pipeline of operations for a data type that represents the arguments (or data) of the operations. For example, to compress an image given a URL:

  type Image struct {
    src string
    bytes []byte
    width uint
    height uint
    err error
  }
  
  func NewImage(src string) Image {
    return Image{src: src}
  }

  func (img Image) Get() {
    if err != nil {
      return
    }
    ...
    // Set error value if this operation fails
  }
  
  func (img Image) Compress {
    if err != nil {
      return
    }
    ...
    // Set error value if this operation fails
  }
  
  func (img Image) Err() error { return img.err }
  
  img := NewImage("https://image.src/random-image.png")
  img.Get()
  img.Compress()
  if img.Err() != nil {
    // handle error just once
  }
Re:Errors in Go: From denial to acceptance by Groxx on Monday November 26, 2018 @07:57PM (#18538344)
this does work... but it also requires two very significant points.

1: you must do this wrapping yourself, for everything you wish to simplify, as few libs do it.

2: you now have non-standard error handling and your tools will not warn you if you handle it wrong.

the second one, to me, is borderline fatal for this pattern. You can't look at this code and realize it's missing error handling (or doing it incorrectly), and you can't run `errcheck` to tell you that e.g. you're missing `err := img.Get()`.

Re:Errors in Go: From denial to acceptance by geodel on Monday November 26, 2018 @04:28PM (#18536946)
And all the code to satisfy borrow checker in Rust, is it for compiler or programmer?
Re:Errors in Go: From denial to acceptance by steveklabnik on Monday November 26, 2018 @05:07PM (#18537267)
That’s like asking “are type signatures for the compiler, or the programmer?” The answer is a bit of both: for the programmer to express intent, and for the compiler to check that intent is correct.
Re:Errors in Go: From denial to acceptance by spenrose on Monday November 26, 2018 @06:18PM (#18537741)
Why would a programmer have an intent about types to express unless s/he had been trained to think of programming in terms of static typing?
Re:Errors in Go: From denial to acceptance by oleganza on Monday November 26, 2018 @06:57PM (#18537949)
If your type system is powerful enough (like in Rust), then you can use it to express relations in your problem domain and have compiler enforce them for you before the code even runs.

Example: https://blog.chain.com/bulletproof-multi-party-computation-i...

Re:Errors in Go: From denial to acceptance by spenrose on Monday November 26, 2018 @08:38PM (#18538582)
I think you missed the "unless" in my question. I am well aware that having chosen to treat types as a first class problem, a coder can leverage that intent. My point is simply that there is a populous, productive world of software development in which typing is a humdrum issue for the coders. C.f. the last 30 years of Javascript and Python
Re:Errors in Go: From denial to acceptance by steveklabnik on Monday November 26, 2018 @07:46PM (#18538274)
What makes that an interesting question? That’s like saying “how would someone have an idea to tests their code unless they were trained to think about testing?” It feels like a tautology, and not particularly relevant.

(I love both dynamic and static typing. I think dynamic type systems are more useful than basic static type systems, but also really enjoy more expressive static type systems.)

Re:Errors in Go: From denial to acceptance by spenrose on Monday November 26, 2018 @08:36PM (#18538570)
Perhaps the programmer should be focused on the purpose the code serves, rather than the code as code object. Mental cycles devoted to thinking about types are, for most programs, overhead. It's perfectly reasonable to be a strong static-typing believer, though I am not. But it's also perfectly reasonable not to think about types when coding, as millions of useful programs have demonstrated.
Re:Errors in Go: From denial to acceptance by steveklabnik on Tuesday November 27, 2018 @09:36AM (#18542561)
FWIW, I've found many people new to programming expect something like types, even though they don't have the vocabulary for it yet. It really just depends on the individual.
Re:Errors in Go: From denial to acceptance by spenrose on Tuesday November 27, 2018 @03:12PM (#18546220)
Sure. And many people new to skiing expect something like gussets to keep the snow out of their underwear when they fall. Issue is whether skiing is about clothing or moving over the landscape. Some focus on your clothes is appropriate, and more in certain circumstances. Rust, which I see you are a proselytizer for (I was at Mozilla during Rust's ramp-up, FWIW), is a tool for writing code that is closely integrated with the operating system. In that context, a type-centric mindset is necessary and valuable. If you are writing code in other contexts: exploratory data analysis, a simple Android app, and on and on, then types are there, but programming is not type management
Re:Errors in Go: From denial to acceptance by steveklabnik on Tuesday November 27, 2018 @06:12PM (#18547763)
Sure, that's why both kinds are useful. I don't disagree at all.
Re:Errors in Go: From denial to acceptance by Skunkleton on Tuesday November 27, 2018 @01:25AM (#18539975)
What do you mean by types? Most modern languages have fairly extensive type systems.
Re:Errors in Go: From denial to acceptance by andreareina on Monday November 26, 2018 @11:31PM (#18539386)
Typing doesn't have to be static (or strong) to be there. When I write `a + b`, I intend `a` and `b` to be things such that the `+` operator makes sense for them. Might be two numbers, might be two strings, might be a string and a number if I'm using js/php, the point is that I always have an intention, implicit or explicit.
Re:Errors in Go: From denial to acceptance by spenrose on Tuesday November 27, 2018 @01:09PM (#18544884)
"When I walk to the store, I intend for the ground to hold me up. My intention might be implicit, but it is always there."
Re:Errors in Go: From denial to acceptance by eptcyka on Monday November 26, 2018 @04:01PM (#18536759)
If it would bring significant amount of technical debt to introduce some synctactic sugar that'd allow to bubble errors up the call stack without 3 extra lines of code, then design choices made for Go seem to be questionable. At least superficially.
Re:Errors in Go: From denial to acceptance by nomel on Monday November 26, 2018 @02:28PM (#18536080)
I find this perspective curious. Maybe it's the non-web field that I work in, but I've never written a program where I could unintentionally ignore errors and have things go as expected after.

What are some cases where errors can be ignored? And if they can be, are these neccesarily some sort of "status" rather than "error"?

Re:Errors in Go: From denial to acceptance by merb on Monday November 26, 2018 @05:41PM (#18537516)
Well look at: https://golang.org/pkg/database/sql/#DB.Query

How likely is the error going to happen, if you know (through tests and everything) that the query is correct?

it can only happen in two scenarios:

1. the database has serious problems 2. the driver is incorrect

in normal cases you would probably handle that with a middleware in classical languages. i.e. you would have a middlewre that catches exceptions and handle the error there (logging, paging, whatever) and maybe show a nice looking "we are experience problems now"-page.

in golang this is a little bit harder, since you would need to handle the error by every caller and with the default http interface you would actually need to call your "handle default error" in every http handler.

Re:Errors in Go: From denial to acceptance by sephware on Monday November 26, 2018 @02:32PM (#18536121)
If you're writing a command line utility that tries to load a config file and falls back on defaults if it's not found, you may try to load it and just ignore any error, only caring that it either returns a path if found or null if you should use default values.
Re:Errors in Go: From denial to acceptance by dwaite on Monday November 26, 2018 @02:45PM (#18536209)
I believe what you are saying is that you can ignore errors if you check or rely on the side effects of an error (such as the result of a function being null on error).

One could argue that the side effect is part of the error, and really what you are ignoring are the details of an error (e.g. did the config entry fail to load because the config file wasn't found, because of filesystem permissions, or because the particular configuration key wasn't present?)

Re:Errors in Go: From denial to acceptance by sephware on Monday November 26, 2018 @02:52PM (#18536239)
That's a good way to put it, and yeah I would agree that's how I like to use errors: give me the details I ask for and let me ignore the ones I don't care about. Sometimes an "error" isn't really an error, and at those times exceptions are overkill. Sometimes an error really is an error and I need to let the user know something went wrong, give them enough details to let them fix it and wait for them to try again.
Re:Errors in Go: From denial to acceptance by Skunkleton on Monday November 26, 2018 @03:41PM (#18536613)
But wouldn't that look like:

   var s Settings
   if s, err := load_settings(); err != nil {
      s = defaults()
   }
Where is the unhandled error?
Re:Errors in Go: From denial to acceptance by TheDong on Monday November 26, 2018 @05:50PM (#18537585)
Note that your code will not work; you've shadowed 's' inside the if block because you have 'if s, err := ...'. As a result, 's = defaults()' will set the shadowed 's' variable that only exists within the if block to defaults, but the 'var s Settings' you declared above will not be modified.

Here's a playground showing the issue: https://play.golang.org/p/eE0HEZx1MJu

Go is full of nice little foot-guns like that :)

You would want to also have 'var err error' above the block and use '=' instead of ':=' to fix this sort of issue.

Re:Errors in Go: From denial to acceptance by Skunkleton on Tuesday November 27, 2018 @09:55PM (#18548914)
I am by no means an expert (or even a frequent user) of go. However, the most unfortunate foot cannon I have encountered is this:

    package main
    
    type HowOdd interface {
    	Poke() string
    }
    
    type AConcreteThing struct {
    }
    
    func (t *AConcreteThing) Poke() string { return "hehe" }
    
    func DoAConcreteThing() *AConcreteThing {
    	return nil
    }
    
    func DoAThing() HowOdd {
    	return DoAConcreteThing()
    }
    
    func main() {
    	lolWhat := DoAThing()
    
    	if lolWhat != nil {
    		panic("How could this happen?")
    	}
    }
    
I understand that the interface at the bottom is a non-null interface containing a null value, but...damn, that sucks.
Re:Errors in Go: From denial to acceptance by danmaz74 on Saturday December 01, 2018 @02:35AM (#18575768)
An example is logging something to a remote server for analytics purposes: you can fire and forget.
Re:Errors in Go: From denial to acceptance by olooney on Monday November 26, 2018 @02:25PM (#18536055)
I agree with you, but it's important to acknowledge the strength of the argument against exceptions. I think Raymond Chen put forth some of the best arguments, claiming they were human error prone:

https://blogs.msdn.microsoft.com/oldnewthing/20050114-00/?p=...

https://blogs.msdn.microsoft.com/oldnewthing/20040422-00/?p=...

While forceful, I don't accept the conclusion of Raymond's argument. I feel it may be true for programmers primarily trained and experienced in C, but is not true in general. As such, the argument reduces to "programmers who are not familiar with exceptions sometimes make mistakes when using them." One could say the same thing about pointers, dynamic typing, lambdas, macros, comprehensions, pass-by-reference, etc. Today, most programmers are familiar and comfortable with exceptions, having almost certainly been taught to use them in college and almost certainly using them on a daily basis for their entire professional career. They'll also have been taught to use a language-specific toolbox such as RAII or try/catch/finally to deal with any potential problems.

Once you get in the habit, it's not hard to scan through a program line-by-line asking yourself "what would happen if an exception were thrown at this point?" and to take appropriate precautions. In fact, I'd wager that for anyone who's spent years working in any language with exceptions this quickly becomes second nature. It's also an easy way to add value during peer code review. In my experience, writing correct code with exceptions is not "really hard," as Raymond argues, but rather no harder and a lot quicker that using explicit error handling. The reason it's a lot quicker is because "exit the function with an error status and do local cleanup of variables initialized so far" is by far the most common correct action. And for cases when its not, the try/catch/finally or other exception handling whatever is straight forward to write and no uglier than explicit error handling would have been. Of course, it's possible I think this because I've worked in languages with exceptions for most of my career. But I certainly don't observe that even the most junior programmers on my team create a ton of bugs that could be attributed to mishandled exceptions. In fact, I can only remember a handful of such bugs, mostly involving open database transactions or unhelpful error messages masking more detailed error messages.

A more solid objection to language support for exceptions can be made by pointing out all invisible code generated by the compiler to correctly handle exceptions. This can lead to significant code bloat and in some cases can negatively impact performance. Or by pointing out how unwise it is to be allocating additional Exception objects while dealing with out of memory errors. These are some of the reasons why C++ isn't appropriate for an OS kernel, for example.

Re:Errors in Go: From denial to acceptance by jstimpfle on Monday November 26, 2018 @04:35PM (#18537010)
> Once you get in the habit, it's not hard to scan through a program line-by-line asking yourself "what would happen if an exception were thrown at this point?" and to take appropriate precautions.

I feel there's a serious disagreement here with how I tend to look at things. I don't want to look line-by-line and ask what would happen in case of an exception. In most cases the answer would be: That would be bad, since the rest of the code would not get executed and the program would remain in an invalid state.

It's much easier to know that there will not be an exception. Simple sequential code, the way it was intended to be by the ancient gods. And handle errors manually where they could in fact occur. I reckon that for my own code, that is only once in a hundred lines or more. Or maybe even more (lines) than that, since I'll abort directly in the callee in many cases anyway, so the caller never gets a chance to see the error. But if there were any point in handling an error in the future, we can still switch to handling it later.

Now you can argue that you can do that with exceptions as well (handling errors directly either by returning an error code or by aborting) such that exceptions would never bubble up. But in that case what's the point in exception handling to begin with? It's just the same now, except that there's a lot of exceptions machinery required in the language.

Re:Errors in Go: From denial to acceptance by renox on Monday November 26, 2018 @06:03PM (#18537664)
Is there such thing as 'simple sequential code'?

Simple? Additions may overflow, printf may fail, etc. Usually code looks simple only because it ignores errors..

As for sequential, we're in the age of multiple cores and GPUs..

Re:Errors in Go: From denial to acceptance by jstimpfle on Monday November 26, 2018 @06:56PM (#18537942)
Of course, sequential code is simple. Overflowing additions don't have anything todo with the code. They overflow or not.

> Usually code looks simple only because it ignores errors..

No, it looks simple when the structure is right. There are other error handling strategies than "handle it now, or pop the stack frame and let your caller handle it now!". But with exceptions that's all the choice you are given (unless you convert them to regular data).

Since exceptions strongly discourage alternative error handling schemes, no wonder they lead to unmaintainable code as soon as a considerable amount of conditions actually should be handled, instead of just bailing out.

> As for sequential, we're in the age of multiple cores and GPUs..

What point are you trying to make? Obviously what I mean by sequential code here is that you can easily reason "if control flow reaches point A, then B will be executed, too".

Re:Errors in Go: From denial to acceptance by codebje on Tuesday November 27, 2018 @12:08AM (#18539580)
> > As for sequential, we're in the age of multiple cores and GPUs..

> What point are you trying to make? Obviously what I mean by sequential code here is that you can easily reason "if control flow reaches point A, then B will be executed, too".

That goes out the door with any parallelism whatsoever, because control flow may reach point A in thread 1, but then thread 2 kills thread 1, and point B is never reached.

It's increasingly hard to bury ones head in the sand and pretend every machine is a single-CPU single-task system with no interrupts.

Re:Errors in Go: From denial to acceptance by jstimpfle on Tuesday November 27, 2018 @07:52AM (#18541893)
Sure, today's CPUs execute not only in parallel but also out-of-order. But that doesn't matter since threads don't let threads die before they've done their share.

Out-of-order makes thread synchronization a little harder (when implementing lock-free algorithms for example), but that has nothing to do with exceptions.

Re:Errors in Go: From denial to acceptance by groestl on Tuesday November 27, 2018 @02:40AM (#18540312)
> and the program would remain in an invalid state.

But this is only ever possible if the program moved to an invalid state in the first place. Granted, sometimes there is no way around that, but usually it can and should be avoided.

Re:Errors in Go: From denial to acceptance by jstimpfle on Tuesday November 27, 2018 @06:05AM (#18541309)
For the programs I write, I'm sure the majority of function invocations do only a partial transformation (i.e., result in an invalid state).
Re:Errors in Go: From denial to acceptance by grogers on Tuesday November 27, 2018 @09:59AM (#18542735)
> it's not hard to scan through a program line-by-line asking yourself "what would happen if an exception were thrown at this point?"

I once implemented a C++ class where some member functions implemented the strong exception safety guarantee. I can tell you, yes it really is that hard. Even when you know certain variables are pointers and can't throw, you are tiptoeing around on razorblades.

At my day job we use exceptions in Java a lot, and it's a good abstraction for rolling back database transactions. But for non-transactional code it isn't the best.

Re:Errors in Go: From denial to acceptance by klodolph on Monday November 26, 2018 @05:44PM (#18537545)
> I feel it may be true for programmers primarily trained and experienced in C, but is not true in general.

If you're mentally comparing against the way error handling in C, your position is much more defensible. Error handling in C requires manually releasing resources in the correct order, and it's easy for a destructor to slip by in the wrong order, to get missed entirely, or for someone to use the wrong jump label. (I'm thinking about Linux kernel style code, here.) If I'm reviewing some error handling code in C, I might have to scan up to the top of the function and count all the resources which need to be released, then look down to the bottom to see that the right label is being jumped to. That's a pain.

In languages like Golang, error handling and resource cleanup are a bit more orthogonal. You can look at a Golang call which returns an error and ask, "Does this handle the error correctly?" Usually this only requires looking at three lines of code at a time, maybe four. Resources are just released with defer, except in unusual cases.

> Once you get in the habit, it's not hard to scan through a program line-by-line asking yourself "what would happen if an exception were thrown at this point?"

I guess I never got in the habit. Maybe you have better habits than I do, or maybe I'm just not "smart enough" to be a programmer, but I find this hard, so I choose to use languages which make this easier. I find it hard in Python, hard in JavaScript, hard in C++, and hard in C#. I'm really not even sure how to e.g. handle exceptions thrown by C++ constructor failures separately from other things later in the block, e.g., I want to do this:

    try {
      MyType x{args, args};
    } catch (SomeException &e) {
      ...
    }
    x.DoSomething();
And I'm not sure how to even write this as functional C++ without doing something weird or maybe just saying "fuck it" and wrapping x in std::unique_ptr.

> In fact, I'd wager that for anyone who's spent years working in any language with exceptions this quickly becomes second nature.

Based on what I see during code reviews, I don't agree. Raymond Chen has a good point... when I review someone's Golang code, checking for correct error handling is fast and easy, it's mostly mechanical, easy to target with lint, and I can immediately move on to looking at the code semantics. With, say, C# code, or especially Python code, there are too many cases where a method call slips inside or outside the try/catch block where it belongs.

I've also been bitten too many times by bugs in production where a function throws an exception, and we have to decode the stack trace to figure out what the error means or how the error is even possible. This is typically easier in programs written in Golang, at least in my experience.

The more syntactic sugar a language has, the more likely it is that I don't notice an error condition that is being handled incorrectly. Golang, with its simplicity, makes you do some extra typing up front but in my experience it pays for itself by going faster through code review and bug fixes.

This depends on the kind of code you're writing. If you're mostly sticking inside application memory and not doing, e.g., IO, IPC, etc, then the exception approach is better.

I'm not saying Golang is perfect by any means, but I find the way it does error handling to be a welcome reduction in my cognitive load.

Re:Errors in Go: From denial to acceptance by repolfx on Monday November 26, 2018 @06:02PM (#18537654)
How is decoding a stack trace easier for Go programs? My experience has been that Go programs simply don't generate stack traces when something goes wrong. Instead you get some log message with a generic "Something went wrong in subsystem X" type message because the error codes got passed up and abstracted by hand. Exceptions with stack traces are far, far superior to that when it comes to rapidly identifying and fixing faults.
Re:Errors in Go: From denial to acceptance by klodolph on Monday November 26, 2018 @08:16PM (#18538465)
In Golang you will typically annotate the error messages with meaningful context as you go up the stack. By comparison, my experience is that often, exceptions only have the stack and one error message, and are missing critical pieces of context. The stack trace will only contain the names of functions, and it will often contain the names of functions that are irrelevant to debugging.

A good error message might look like this:

    Could not travel to Alpha Centauri:
      warp drive initialization failed:
      power coupling 397 is offline:
      temperature out of range (temp = 150°C)
A bad stack trace might look like this:

    DeviceClient::Connect()
    std::promise::something
    <anonymous function>
    DeviceSet::PowerOnAllDevices()
    <anonymous function>
    std::etcetera
    std::etcetera<some_big_thing, more_params, allocator=std::allocator>
    TravelCommand::Engage()
    VoiceCommand::RunCommand()

    Error: temperature out of range (temp = 150°C)
Stack traces are useful, but they're a poor substitute for good error messages. There's just too much context missing from raw stack traces (which file, which URL, which device) and too much noise (anonymous functions, higher-order functions, etc.) That, and a stack trace often requires reading the source code to interpret properly. That might not be possible or it might be outside the skill set of the person interpreting the error.

Exceptions optimize for the case where the exception bubbles straight up to some handler, but I'd rather not have my errors work that way. It's incredibly common for it to make no sense to bubble an error up without some kind of annotation or additional context.

Re:Errors in Go: From denial to acceptance by pvg on Tuesday November 27, 2018 @12:05AM (#18539570)
This doesn't seen like a particularly fair comparison because bad error messages (if we are to compare them to a bad stack trace) look like this:
Re:Errors in Go: From denial to acceptance by klodolph on Tuesday November 27, 2018 @01:48AM (#18540074)
I'm not sure if you're trying to make some kind of joke. An empty string is a bad error message, but you would have to go out of your way to make this happen in Go, you would have to do something obviously wrong like

    return errors.New("")
You have control over whether the error messages are good or bad, you just have to insert the right annotations where the critical pieces of context are known.

Stack traces are not like this, you can't really control whether they are good or bad, at least not well. If you want to make your stack traces more informative what do you do? You can't really change them, they just reflect your call graph.

Golang optimizes for the case where you get good error messages. Exceptions optimize for the case where you don't care about adding context to the error and just want to bubble it upwards. On the balance of things, I prefer Golang's approach because most of the time, I don't want to see the stack trace.

Re:Errors in Go: From denial to acceptance by pvg on Tuesday November 27, 2018 @02:01AM (#18540137)
Exceptions in many languages carry messages and exceptions can be nested. There's also nothing stopping you from logging messages as you handle exceptions. The best case of exceptions is easily as good as best case go-style error handling and reporting. The worst case of exceptions is far more informative than the worst case of error messages. I'm not sure what an example that requires great use of some language facility is supposed to be illustrative of - good use of a facility is usually... good?

[Edit: this was a reply to a comment you seem to have rewritten (I think for the worse) but I think the response still mostly works]

Re:Errors in Go: From denial to acceptance by lloeki on Tuesday November 27, 2018 @06:40AM (#18541470)
I think this can fundamentally be summed up as:

errors are "failure as data" (hence, message), and you get to write the flow (including handling and annotating the call stack)

vs

exceptions are "failure as flow" (hence, stack trace), and you get to write the data (including logging)

May very well be paraphrasing a well known maxim about closures and objects as errors are a poor man's exceptions and exceptions are a poor man's errors.

Re:Errors in Go: From denial to acceptance by codebje on Tuesday November 27, 2018 @12:11AM (#18539592)

    try {
      MyType x{args, args};
      x.DoSomething();
    } catch (SomeException &e) {
      ...
    }
You can't do much with the x if it didn't construct correctly, so you either need a strategy for a safe default value for x, or you need to bail entirely if an exception occurs.