@alexmu But we do know the right level! They are the boundaries at which we accept the user data coming from outside the system. For example in Haskell or Java, I'd propagate such errors to a boundary and make a decision there instead of crashing at somewhere deep in in the program. I think it is a culture thing.
very long blather about rust errors
@abnvAbhinav ๐
@alexmu so .. folks don't always discuss this in presentations of rust errors (and I'm not here to totally defend where rust errors wound up) but the question of recovery boundaries was _central_ to the design discussions and iterations.
many of us had experienced working in C++ (or eg. Java) where "pervasive exceptions from any possible expression" + "lots of mutable state that touches all other mutable state" meant that there was rarely any place in a real-world try/catch programs where a catch would actually wind up with a program in a safe, non-corrupt state.
(there's a whole literature about this in C++ called "exception safely levels" -- https://en.wikipedia.org/wiki/Exception_safety#Classification -- which of course nothing can check or reason about statically and most programs completely fail to adhere to. It's extremely hard to even consistently write exception-safe C++ destructors.)
in addition, there are a bunch of things in C++ at least that _don't_ throw, but _do_ kill the process. and that's going to be true in most systems languages. rust has unsafe! and if you segfault or execute an illegal instruction your process is toast.
given that, we saw and continue to see a lot of programs adopt "process boundary" as the safe(r) boundary for "major error recovery". web browsers and many network servers for example tend to do process separation. so there was an argument that "unrecoverable error that kills the process" is probably a good primitive concept to include, and for super-pervasive errors they should probably be routed into there.
but of course, not all errors are fatal, and some are a bit more "expected" and _some_ seem meaningfully recoverable (eg. see the distinctions between checked and unchecked exceptions in Java), and so we iterated a bunch on the question of lesser types of error (including a condition-handling system a bit more like common lisp) and ultimately landed on the result type, and a copy of Swift's ? operator for propagating it, which is .. eh .. ok? not great but at least fairly explicit and well-marked, and easier to reason about than "every expression everywhere might throw mid-evaluation".
the result is a bit of a muddle. error and result shipped with too little support for abstraction and composition, and there are probably too many ways to panic, and unwinding and unwind-catching is a thing of arguable utility (I'd kinda prefer to remove both). but the whole design does have a rationale behind it, it's not just foolishness. at worst I'd say "they shipped something incomplete under time pressure and limited information about how it'd wind up being used".
If you have a fediverse account, you can quote this note from your own instance. Search https://types.pl/users/graydon/statuses/115578228137522660 on your instance and quote it. (Note that quoting is not supported in Mastodon.)