4 ms·
There are options between these two extremes. Haskell's Except monad is a great example. Or javascript promises. You can launch a bunch of actions in sequence a
by embwbam 9y ago
There are options between these two extremes. Haskell's Except monad is a great example. Or javascript promises. You can launch a bunch of actions in sequence and provide a single error handler for them. Each step doesn't have to worry about it, but neither is the error handling invisible and completely global.
- acjohnson55 9y agoYep. For my money, the war is over, and the monad won. I shouldn't say the m-word, as it turns people off. But that's the unifying pattern. 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.
- girvo 9y agoThey have, sort-of! 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. [0] http://blog.briancavalier.com/creed/ http://blog.briancavalier.com/creed/
- haskellhater 9y agoError handling in Haskell is, in practice, terrible. Not only do you constantly have to wrap and unwrap potential error values to unify different error types, you also have to deal with a dozen variants on Either and related typeclasses, none of which are used consistently across the ecosystem. And as if that wasn't bad enough, there are also actual throw-catch-style exceptions, which are not checked in anyway and, combined with laziness, can explode in your face at absolutely any time whatsoever. It's a big big mess. 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. Guess how well that went most of the time. 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.
- pcwalton 9y ago> Not only do you constantly have to wrap and unwrap potential error values to unify different error types 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 https://github.com/go-errors/errors, which has the exact same issue you describe with dependencies.
- zaarn 9y ago>This is also the case in Go (panics and recover). 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)
- haskellhater 9y ago>> Not only do you constantly have to wrap and unwrap potential error values to >> unify different error types > > This is a legitimate issue, although in Haskell you can use one error type > like Go does if you want to. 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 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.