Earlier quoted context omitted.
>It's designed to force people to handle the damn error as near to the call as possible. But that is sometimes the wrong design. If you have functions A() --> call B() --> call C() ... and C() has an error because of a memory allocation failure or a network connection being down, sometimes the best context to handle that error is the outermost function A() and not C(). That's why some programmers don't like copypasti…
What if we designed a system where there are two kinds of failures that can be returned? One where the caller is forced to address it by the compiler, and one that is transparent to the caller, but can be caught and addressed by anyone in the call stack (probably the top level)? And one could convert one type of failure to the other. So if you call a library function and it returns the force-you-to-address kind of er…
In some languages the distinction is between logic errors and runtime errors. In Java, checked and unchecked exceptions. In Go, err and panic. Rust also has Err() and panic!().
If you look at, say, the evolution of the “if” statement, it was a number of years before this “obvious” control structure was added to programming languages. So there might be something similarly obvious for error handling, we just haven’t figured it out yet.