Anyway, Go-style error handling's lack of any of these issues is what makes it attractive to me: it's simple, straightforward and get's the job done. Granted, with a bit more typing and the occasional bug that could have been avoided by not having to type the (almost) same three lines once again, but not having to jump through any of the hoops I keep encountering in more sophisticated languages is so, so worth it to me. I feel like Go stays out of my way, lets me focus more on actually getting things done than on dealing with the language's intricacies.
This is a legitimate issue, although in Haskell you can use one error type like Go does if you want to.
> And as if that wasn't bad enough, there are also actual throw-catch-style exceptions, which are not checked in anyway
This is also the case in Go (panics and recover).
> I stopped writing Haskell maybe a year ago, but at least back then getting GHC to print a stack trace when an exception wasn't caught was a major feat that involved recompiling every single one of your hundreds of dependencies with profiling flags.
This is also the case in Go. Errors do not retain anything about their state. You need to use a package like https://github.com/go-errors/errors, which has the exact same issue you describe with dependencies.
It is considered good style to not let a panic escape your library API unless something just went very severely wrong that the library could not possibly continue to function normally.
>This is also the case in Go. Errors do not retain anything about their state.
Errors are an interface, you can put any state inside it you want and I've frequently done so (retain line counts in parser or network data for example)
SomeException is pretty much equivalent to Go's error, but it's not commonly returned by libraries and such so using it barely helps with the need to convert often. In addition all the types used by libraries as error values now need to have Exception implemented, which they don't. So, in practice, no, you cannot. Or rather, it wouldn't solve the core problem and create a new one.
>> And as if that wasn't bad enough, there are also actual throw/catch-style >> exceptions, which are not checked in anyway > > This is also the case in Go (panics and recover).
True, panic/recover exist, but in practice I find them to be more akin to setjmp/longjmp in C than throw/catch-style exceptions in other languages. Off the top of my head I can't recall a case where I had to recover from a panic that I didn't create in my own code (i.e. originated in a library call). Exceptions in Haskell and other languages on the other hand are usually all over the place (ok, usually not in pure code in Haskell) and I feel like I have to constantly keep them in mind to avoid letting one slip through the seams and then later having to track down where the hell that one came from.
>> I stopped writing Haskell maybe a year ago, but at least back then getting >> GHC to print a stack trace when an exception wasn't caught was a major feat >> that involved recompiling every single one of your hundreds of dependencies >> with profiling flags. > > This is also the case in Go. Errors do not retain anything about their state. > You need to use a package like https://github.com/go-errors/errors, which has > the exact same issue you describe with dependencies.
That's fair and indeed it would often be handy to know where an error came from. But since errors are just return values I don't really expect them to have a stack trace attached to them, just like I don't expect any other value to have that. It would still be neat for debugging sometimes, more so for errors than other values. I think there are some proposals for improving on this in Go 2. What really frustrated me about Haskell though was debugging throw/catch-style exceptions without a stack trace, because I basically had zero clues about where it came from and there were no clear paths to trace in the code. In Go on the other hand, errors don't just plop up from out of nowhere and panics, which can, print a nice stack trace when not recovered from.
So yes, it can be messy when using other people's code because this is very much an unstable part of the language right now. I still prefer that over the worst possible strategy (what Go picked) being effectively baked in.
Javascript messed it up a little bit with promises, because it's so easy to swallow errors. IMO, they should have made it so that unhandled promise rejections throw by default at the next turn of the event loop, with a method that could be called to opt out individual promises.
At least, V8 did IIRC. I use Creed[0] myself, which using it's `shim()` function gives you the same behaviour. It's quite lovely.