4 ms·
> it's too easy to forget an error check. I never understood this. I do understand it is easy to forget to handle an error like it is easy to forget to handle
by randomdata 3y ago
> it's too easy to forget an error check.
I never understood this. I do understand it is easy to forget to handle an error like it is easy to forget to handle a distance or temperature. Humans will make mistakes.
But nobody laments about how they forget to handle distances or temperatures, or cry for special constructs to ensure that they don't screw up their distance and temperature handling. What is it about the word error that sends developers into a tizzy?
Why would we want a solution that only works for errors and not for values of all kinds?
- vacuity 3y agoBecause handling errors is important for complex programs. Whether errors-as-values or exceptions or aborts are the best option, being able to write a program that handles errors well makes it that much more robust. Don't want some weird behavior happening when the program should've noticed an error and dealt with it somehow.
- randomdata 3y agoHanding values is important for all programs. You don't want some weird behaviour happening in any case. If you are writing a system that turns on a heater when the temperature falls below freezing, you're going to have a really bad time if you forget to handle the temperature. So, yes, it makes sense to have constructs that can help with that problem, but it would be weird to have such constructs only for what a human considers an error condition. You would want that for every case that needs to be handled. Otherwise you have to resort to testing, and if you accept that testing is good enough to ensure that you don't forget to check the temperature, then it also good enough to ensure that you have checked an error. There is nothing special about the error case. It's just another state like any other.
- vacuity 3y agoWell, I think that's why a lot of people are disappointed that Go doesn't quite have sum types, because sum types do increase expressivity around values in general. That they improve error handling is just one application.
- randomdata 3y agoThere seems to be no consensus as to what sum types actually means, but the most popular definition I find in my travels is: Tagged unions. Which is funny as a sum is not a union, but anyway... Assuming you share in that definition, while Go might benefit from tagged unions in general, I'm not sure they actually help in any way with remember to handle errors. A union is just as easy to forget to handle as a single discriminate type. But perhaps you are of another sum type school? Perhaps one of the other sum type definitions are useful here?
- vacuity 3y agoTagged union is a good definition. I was thinking of Rust's Result type, which has Ok(some value) and Err(some value) variants.
- randomdata 3y agoRight. See that doesn't help, at least not in the context of Go without any other modifications. Logically, you can reduce that Result state to a simple boolean (Ok = true, Err = false). But the bool type in Go is prone to the very same problem. It doesn't matter how many type layers you add. The problem is that there is nothing in the language that enforces handling of values, no matter what type that value might be.
- vacuity 3y agoYes, manual tagging doesn't provide type safety. That's why I said some people are disappointed that Go doesn't have sum types. There isn't even a convoluted workaround either, at least not a full one.
- randomdata 3y agoIt is not so much a matter of lack of type safety, it is forgetting to implement an application requirement. Even an advanced type system capable of formal proofs isn't going to help you if you've forgotten the requirement when defining the types. And if you are using a language with a lesser type system (Go, Rust, any language you are likely to use in the real word), and haven't forgotten the requirement, then you would have encoded that knowledge in tests instead, making the forgetfulness in implementation ultimately immaterial. After all, there is no practical difference between learning that you forgot to implement logic by the compiler telling you or the test suite telling you. So, we can infer that the OP is talking about where one straight up forgot about the requirement from start to finish. I'm skeptical that there is any technical solution to that problem, but sum types most certainly isn't it.
- erik_seaberg 3y agoAn error signals that a non-error value you were relying on will not be available. Control flow should nearly always change in response to that, often drastically. That’s why a language should provide a caller-controlled non-local exit from a futile chain of calls. Many languages only do that for errors, which is not a major limitation when users can define errors.
- randomdata 3y ago> An error signals that a non-error value you were relying on will not be available. Except the non-error value should always be available, even if that means relying on the zero value. Zero values should be useful. An error state is an independent state. It should also be useful without any other values. While the caller may choose to use the error state to make decisions about other state, it is faulty API design to force that upon the caller.
- erik_seaberg 3y agoMany domains have no meaningful zero value. If “getUserByID” returns a fake user, anything you do with him will be incorrect, if you don’t panic and die (as zeroes of builtin types often do).
- randomdata 3y ago> If “getUserByID” returns a fake user Logically, getUserByID returns nil when there is no user, not a fake user. The caller can then check for its nil-ness without needing to consider the error value. nil is a useful value. For better or worse, nil is how Go convention represents the absence of a value. In fact, convention sees error also rely on nil to be useful in the very same way. It's funny how people completely forget how to write software as soon as they see the word error. Why is that?