Comment by sieve
2 days ago
In the vast majority of programs, errors and non-results are irrelevant at the call site. You want to program for the happy path and not wrap every single call with error checking code or `try/catch/finally`.
h = file_open(path)
data = file_read(h)
file_close(h)
return data
is the code you want to read. This essentially is:
return file_open(path).success(h -> h, file_read(h)).success(h, data -> file_close(h); data)
A language with thoughtful error handling primitives will support such code.
> You want to program for the happy path
This is the root of a lot of the evil. Yes, humans want to program for the happy path. It is a lot more fun. However, that bias leads to so much lack of diligence to properly deal with the unhappy path.
Syntax is not going to solve logic errors. No amount of checked exceptions or forced unwrapping of a result will help with TOCTOU or any number of similar vulnerabilities in code.
You ideally want to attack the problem at two ends:
- make the syntax noise-free so you can see what is happening
- build primitives at the syscall and stdlib level for common footguns
You would still need to deal with domain-specific logic errors, but you have at least reduced the surface area exposed to casual attacks.
> This is the root of a lot of the evil
It is not. Being extremely restrictive in the happy path, throwing all over the place, but have generic error handling code at the top level (e.g. HTTP entry point) makes for some pretty robust, stable, easy to read software. Albeit it typically has somewhat rough user experience.
The thing is, that's a pretty good place to start. From there on, you can handle exceptions one by one, surgically, and make things nicer.
I keep hearing how diligent it is to handle errors at the call site, always by people who end up making services that either crash for preventable reasons or bury errors. Python is actually safer than Golang, and I don't mean with type hints.
People using exceptions want to handle the unhappy path, just usually not at the call site.
that is the same as saying the method throws IOException
It might throw one. There is a difference between documenting what exceptions can be thrown and forcing every consumer to handle them.
returning a non-success you either handle it or pass it up the chain, potentially with not a lot of information about what originally caused it. with an exception you can retain the full information about what happened by either rethrowing it, throwing your own exception with it embedded as the cause or declaring that you don't handle it by saying you can throw it.
2 replies →
Yeah, checked exceptions are weird. Almost everything has some way to fail. The exact type of exception is usually meaningless, more like there are a couple of broad categories analogous to HTTP 4xx vs 5xx, and even then you usually don't want to handle it right there.
IOException must be #1 on the list of most ignored exceptions. Like okay what am I supposed to do really I'm just trying to run a select against my db
Well, for example, if you had opened a file to put the data into, you might delete the file or restore from a backup. But even if the answer is nothing, bubbling it all the way up to main is not the wrong answer. The point isn't whether you can do something, it's whether someone can do something.