7 ms·
Most programs don't need generics, but a lot of programs benefit from having them. This, along with exceptions, are the two things that keep me from considering
by collinvandyck76 12y ago
Most programs don't need generics, but a lot of programs benefit from having them. This, along with exceptions, are the two things that keep me from considering Go for future service development. We've stood up a couple of successful Go services, but the maintenance overhead from not having these two things has been substantial.
- seanmcdirmid 12y agoGo has something that is functionally equivalent to exceptions; they just don't call them exceptions and make them look a lot different from what we are used to.
- mwsherman 12y agoYes and no, errors are just return values. They’re not special, they are conventional. So you get the whole type system to express errors – they’re just values. And you don’t have a different control flow. It’s not without warts. You can bail with panic(), which is essentially a throw. It’s tedious to handle errors, but that’s because actually handling errors is tedious.
- eropple 12y ago"Actually handling errors" isn't tedious at all in Scala. Chain flatMap() across methods that use Try[A], handle the Failure[_] at the other end, and you're done. I'm not joking when I say it really is that easy. Go makes it (and, really, many other things) very difficult for reasons that are at best murky.
- NateDad 12y agoSo you call say 3 methods, and at the end you have a "file not found" error... which call resulted in that failure? I don't know Scala specifically, but from what I know of most languages that support this kind of chaining, you can't tell. And that's the problem. Did your initialization fail to find it's config file in step 1? Did you fail to find the target file you were going to transform in step 2? Was there some other failure in step 3? You can't tell, so you can't actually handle the error. This is effectively like doing this in Java: try { DoX() DoY() DoZ() } catch( Exception ) { // The code has no idea what failed here. } Whereas, the go code looks like this: if err := DoX(); err != nil { // handle error from DoX } if err := DoY(); err != nil { // handle error from Doy } if err := DoZ(); err != nil { // handle error from DoZ } This is what Go programmers means when they say "actually handle the errors". At each step you handle the specific failure from the specific call. It's somewhat more verbose, but it's a LOT more robust against real life failures.
- pcwalton 12y agoYou'd have the same effect (you don't know what specific call failed) if all of the comment lines said "return err", which is a common pattern in Go. The Scala approach mentioned is just sugar for that. You can of course handle each case separately if you want in Scala, just as you can in Go, but the common pattern has sugar.
- frowaway001 12y agoWhat? Actually, absolutely not. If you only care about whether something succeeded use Option, if you care about the error use Either, Validation, etc. Just pick the right Monad.
- pcwalton 12y agoYes, I agree, but I don't see how that's related to what I said.
- frowaway001 12y agoThen maybe don't comment?
- eropple 12y ago> So you call say 3 methods, and at the end you have a "file not found" error... which call resulted in that failure? The first exception hit pops out the other side and you pattern-match against it. So, the one that did file access and returned a FileNotFoundException. If you have multiple pieces of code that can return FileNotFoundExceptions, you can pass a message in the exception, just like any other Java exception. You can often be more type-specific, too. Bear in mind that defining a new exception in Scala is a one-liner, and you can encapsulate your FileNotFoundException in a RetrievalFailedException very easily and cleanly. Your method is not more robust, I assure you--it's just verbose and both typo- and thinko-prone.
- gouggoug 12y agoErrors are just return value, but in go, you either explicitly ignore the error returned (using the "blank identifier" _ ), or, you assign it, in which case you have to deal with it (otherwise go will complain about an unused variable). An that is what makes go awesome. In java or php, you, as a developer, can never know wether the functions you are calling will throw an exception. The only way to know? Read the doc, if you're lucky and there's a doc, or read the code... The result? your program will crash if you didn't add your try..catch block. Go forces you to either explicitly ignore errors (and your fellow co-worker will know you did it on purpose) or deal with them. No surprises.
- Peaker 12y agoI get the same in C, with gcc's __attribute__((warn_unused_result)).
- aktau 12y agoThis comment should be more prominent, warn_unused_result is pretty awesome!
- tmhedberg 12y ago> In java or php, you, as a developer, can never know wether the functions you are calling will throw an exception. That's what checked exceptions are for in Java.
- gouggoug 12y agoThe larger problem with exceptions, checked or not, is that they interrupt the flow of your program, which is too bad since most exceptions are not that exceptional. That problem is exacerbated by developers who don't know how and when to throw exceptions and end up throwing exceptions for easily recoverable errors. The other thing I dislike about exception is that your code ends up with tons of try..catch blocks.
- disputin 12y agoIt's that convention and exclusion of special abstractions that appeals to me by keeping the cognitive load to a minimum. I can focus on the problem without thinking about the language. If Go became like various other languages, then what would be the point? We already have Java, C#, ...
- collinvandyck76 12y agoIf you're talking about panics, you can only handle them in defer funcs if I'm not mistaken, which makes them significantly different from exceptions that I'm used to that can be caught in any part of the code. The dependency on defer in my opinion makes them unusable as typical exceptions in my case. This was perhaps the intent, making panics and the recoveries thereof truly exceptional and painful, pushing one towards the normal error handling semantics, which is what I dislike.
- cmelbye 12y agoIt's true that you can only handle them in defer funcs, but the defer func could be anywhere up the stack, not just the current function you're inside. (I'm not sure if that was your understanding or not) "The panic and recover functions behave similarly to exceptions and try/catch in some other languages in that a panic causes the program stack to begin unwinding and recover can stop it. Deferred functions are still executed as the stack unwinds. If recover is called inside such a deferred function, the stack stops unwinding and recover returns the value (as an interface{}) that was passed to panic." https://code.google.com/p/go-wiki/wiki/PanicAndRecover https://code.google.com/p/go-wiki/wiki/PanicAndRecover
- dragonwriter 12y ago> If you're talking about panics, you can only handle them in defer funcs if I'm not mistaken, That's true...but no different from "exceptions can only be caught in catch blocks". > which makes them significantly different from exceptions that I'm used to that can be caught in any part of the code. It doesn't make them any different from exceptions -- just as a catch block can be anywhere up the call chain, a deferred function can have been set anywhere up the chain. Defer is basically "finally", except the position is different, and "recover" inside it lets it also do what "catch" does.
- dragonwriter 12y agoThe only real significant difference between panics and exceptions in the Java/Python/etc. style is that -- reflecting (apparently) the same philosophy of explicit-over-implicit that governs other Go error handling -- the common catch-one-kind-of-exception-and-implicitly-rethrow-everything-else idiom of the Java/etc. language is inconvenient and verbose to express in Go (though if you really want to use that idiom a lot, it seems like it should be implementable in a library function to create that kind of handler that would then eliminate the boilerplate on each use, though given Go's convention on panics generally not crossing public API boundaries, you shouldn't need that idiom as much in Go code.)