This is one of the main reasons why I personally try to avoid exceptions and instead encode failure into the type system. For example, returning a Result<'t,string> rather than just a 't. (You can refine the type further). Altough, in most languages that approach results in a lot of boilerplate.
How is this different in practice? Checked exceptions are simply "syntactic sugar" for error types (exception objects with a few special handlers to return them and receive them).
You can write almost exactly the same code with your Result by ignoring the error until the point you would catch it with exceptions.
The only difference is that exceptions bubble up if unhandled, which requires a generic catch to match code ignoring errors otherwise.
It is quite a bit more explicit in that something can have an error and in what ways it can error (if you enrich the type in that way), while still being ergonomical. And it reserves exceptions for the truly unexpected/exceptional cases.
Are we comparing the same things? Adding exceptions to the method signature is pretty explicit to me too.
It's also pretty ergonomical depending on how you generally treat errors you don't care about.
I do find your point about exceptions being for unexpected things interesting: basically, split errors into two classes and use separate mechanisms for each.
I generally feel that this differentiation would never hold between different codebases (an HTTP library may consider network error something "normal", but users of that library might consider it exceptional), but it's surely an interesting concept.
I also think that we as developers are pretty bad at handling both expected and unexpected errors, so having two mechanisms will only make us less likely to do so (not by much, though).
FWIW, my take is that they are largely equivalent except for more or less syntactic support for the approach.
> FWIW, my take is that they are largely equivalent except for more or less syntactic support for the approach.
Yeah, as with lambda calculus and turing machines, they are equivalent in capacity.
> Are we comparing the same things? Adding exceptions to the method signature is pretty explicit to me too.
Maybe, maybe not. IME the IDE support for exceptions is quite a bit worse than for returning results and such.
> I do find your point about exceptions being for unexpected things interesting: basically, split errors into two classes and use separate mechanisms for each.
> I generally feel that this differentiation would never hold between different codebases (an HTTP library may consider network error something "normal", but users of that library might consider it exceptional), but it's surely an interesting concept.
Isn't the two ways to handle it already a thing with checked and unchecked exceptions, though? As in having wrappers that convert one to the other.
> I also think that we as developers are pretty bad at handling both expected and unexpected errors, so having two mechanisms will only make us less likely to do so (not by much, though).
I don't quite agree with that, as with encoding it in the type system conveys more intentionality than with exceptions, IMO.
> I don't quite agree with that, as with encoding it in the type system conveys more intentionality than with exceptions, IMO.
Sure, but why keep using exceptions then at all? I am fine with returning errors making them explicit, but I would avoid exceptions everywhere (Go-style).
In F# it works quite well, though.