I think it's nice to have two mechanisms for errors like in ML: sum types for errors that the calling function can realistically recover from, and exceptions for the rest.
Yes, and you can even handle some errors, and then capture an error whose type is the subset of unhandled errors, and return that, limiting the error set of the current function to not include the handled ones. Here are a couple examples: https://github.com/ziglang/zig/blob/7d0de54ad44832589379a4bc...
You get compile errors if you try to handle an impossible error, or don't include an `else` prong (in which case the compiler tells you the full set of unhandled errors).
This is Rust's approach, too: realistically recoverable errors are handled with Result<T, E>, and other are handled through panics. (Panics are generally implemented as exceptions, i.e. they unwind the stack, but the compiler can also be configured to make them just abort.)