4 ms·
I don't know what codebases this covers, but GOOD Go code does not panic.
by NateDad 12y ago
I don't know what codebases this covers, but GOOD Go code does not panic.
- sigzero 12y agoGOOD <insert your language here> code does not panic. Except people write code...
- dllthomas 12y agoBut this seems more like "good Haskell code does not use unsafePerformIO" than "good C code does not segfault" - if you can find violations with a grep, it's much less of a concern to rely on programmers to get it right and not use it where inappropriate (which is not to say it's not a concern at all).
- barrkel 12y agoWhen something has an error, you have approximately four choices: 1) fix it locally 2) terminate 3) ignore 4) delegate decision to caller (1) is rarely an option because failure conditions are usually beyond the control of the low-level code performing the operation. That's because error conditions usually occur at the edges of an application, typically I/O of some sort, and typically are the implementation of a higher-level semantic action. If you're trying to open a file or connect to an address, what can you reasonably do if the file is missing or the network is unavailable? (2) is not an option for systems beyond a trivial size, unless the system is composed out of little executables. There's an argument for going this route in an actor system, but most Go apps are not actor systems. (3) is usually only an option for throwaway programs - they're probably better off terminating, in case they do something bad after preconditions have been violated. Which leaves (4) - delegation to the caller. This is the most composable option, since it lets the composer of the components control the error strategy. In its most flexible form, this looks something like Lisp conditions; it's easy enough to emulate with a stack of error handler callbacks, but the benefits only come when the handling system is consistently used everywhere. The more common version is to unwinds the stack until someone is able to deal with the problem. Whether you unwind explicitly with checks to an 'err' return code, or implicitly with panic, or with longjmp, or with exceptions in a language with that feature, doesn't really matter. You're still doing the same thing, semantically and with code flow. Which error handling strategy is more error prone? The most manual one - the one that makes it easiest to drop the error on the floor, or otherwise molest it on its path from cause to handler - would be my bet. Java's pathology here is statically checked exceptions; this encourages molestation of the error on its path, since the error value potentially needs to be of a different type at different points in the code flow. This risks obscuring the cause of the error, since errors are a function of implementation - but Java encourages expressing errors in terms of interface. C++'s pathology is that it allows code flow through user provided code that is ill suited to throwing exceptions. The added complexity spawns whole books worth of material on how to write C++ code in an exception safe manner. Good exception handling style in languages with sane exceptions normally means almost never catching exceptions except for high level orchestration routines; reacting to unwinding by cleaning up resources; and generally taking a transactional approach to operations. I believe that good error handling with explicit error codes doesn't do anything semantically different in response to an error - it just does it all manually, i.e. in an error-prone fashion. And it's my biggest problem with Go.
- NateDad 12y ago"Good exception handling style in languages with sane exceptions normally means almost never catching exceptions except for high level orchestration routines; reacting to unwinding by cleaning up resources; and generally taking a transactional approach to operations." I agree. Except that is really really really hard. How does your orchestration routine know to close the file handle that was opened 6 levels deep in the code? How does it know to return 404 not found to the request that was being handled 4 levels deep? It can't, and if it does, then you're breaking encapsulation. The reality of the situation is that the only place where you have the knowledge to clean up after a function is inside that function. I suppose in theory you could write a whole crapload more code to create auto-clean-up objects and transactional frameworks.... and that'll take a lot longer and be a lot buggier, and be a lot less flexible than just handling the error near to where it was produced with boring, straight-forward code, which is how it works in Go.
- dragonwriter 12y ago> Except that is really really really hard. How does your orchestration routine know to close the file handle that was opened 6 levels deep in the code? It doesn't need to. Which is why "never catching exceptions except for high-level orchestration routines" is a separate point from "reacting to unwinding by cleaning up resources". What is being said is that, in good exception handling style, Orchestration routines use try/except (they might use "finally" if they also have local resources to clean up), and lower-level routines use try/finally without "except". > and that'll take a lot longer and be a lot buggier, and be a lot less flexible than just handling the error near to where it was produced with boring, straight-forward code, which is how it works in Go. With Go, you pretty much do the same thing -- lower level code cleans up its own resources (with defer in place of finally) and will propagates errors up in the same circumstances that code would in other languages (with explicitly error returns rather than implicit propagation of exceptions), and higher level orchestration routines are mostly where you do something more with the error returns.
- NateDad 12y agoOK, so I don't think we're disagreeing at all then, except possibly about the difficulty / commonality of writing the correct exception handling code.