4 ms·
There's another approach that I don't see discussed often: structure operations so that code just "passes through" on errors and code the happy path. In the ca
by hsn915 3y ago
There's another approach that I don't see discussed often: structure operations so that code just "passes through" on errors and code the happy path.
In the case of opening a file, you would get an invalid file handle that you can still pass around to functions and nothing would "fail"; it would just be as if you passed a handle to /dev/null. This scheme requires that you can still explicitly check if the handle is valid.
Most people are familir with some form of this with the special NaN floating point value.
- iainmerrick 3y agoI find NaN mostly awkward and annoying in practice. Writing graphics code, you often find that some rendering step just fails completely, and it's because you had a NaN (almost always divide by zero) somewhere along the line. Trying to pin down exactly where it occurs can be a real pain. Rather than silently passing NaN along, it would be much more useful to fail immediately and loudly. Therefore, I'd be very wary of using this pattern elsewhere, unless you can carry along a backtrace to help track down the culprit. On the other hand, it is useful that GL shaders will still sorta-kinda work even if you have zero divides, rather than completely blowing up. In many cases this only happens on a single pixel with very specific inputs, and you can just ignore it -- like having a single dead pixel on a monitor.
- kevin_thibedeau 3y agoNaN does it's one job when it is annoying. It exposes errors at their origin before they have an opportunity to propagate. If you're generating NaNs your code is broken.
- iainmerrick 3y agoAt their origin? How so, when you can just keep calculating and you don't get exceptions, just more and more NaNs? Edit to add: like, if the error is "distance was zero so I couldn't invert the matrix", I want that to be the error feedback; not "the screen is black because the final result of all the camera calculations was NaN".
- masklinn 3y agoThat's because it tends to be wonky and end up in tears as you try to find out what the source of the corruption (the original creator of the error) is. NaN is emblematic of that, as it's usually undesirable and you'd much rather have received an error instead of gotten nonsense injected in your program state. Objective-C has this, where errors would usually end up returning a `nil` (with an NSError accessible via an optional out parameter), and sending a message to `nil` has no effect and returns `nil`, so you could code on the happy path and end up with no idea whatsoever why your thing stops working, with the only information being that you end up with a `nil`. libc error handling also kinda but kinda not uses this pattern, with the additional fun that you can UB on the way: libc does not normally reset errno, so you can reset it (to 0), call a bunch of functions, and check errno at the end. Except that doesn't really work because: 1. not all functions will signal errors via errno 2. third-party code may be resetting errno for its own use (errno is not unlike a caller-saved register in that sense) 3. third party code may handle errno internally, but will not reset it if so, leading to sprurious error messages.