5 ms·
> Safer because unlike exceptions, they force callers to handle all potential outcomes, but no more. Which is bad. In 99% of cases I have no specific error han
by The_Colonel 3y ago
> Safer because unlike exceptions, they force callers to handle all potential outcomes, but no more.
Which is bad. In 99% of cases I have no specific error handling for the given problem. Just let the process crash or be handled by application server / framework.
Representing these kinds of error conditions in the code which I have no interest in handling is just noise.
- titzer 3y agoSituations vary a lot. In general there's no guarantee that a far outer scope is going to know how to deal with errors from deep inside some nested calls except for some very general logic, like failing a whole operation.
- kaba0 3y agoEven more commonly, there is no meaningful error handling to be done at a layer above the call.
- bedobi 3y agoI don't mean to be uncharitable but it seems like most exception advocates here don't really understand the case us functional error handling advocates are making. We don't advocate handling errors at every level. They're usually sent to the boundary layers of the system, eg the http resource layer, and folded there into http responses.
- bedobi 3y agoThe problem with that is you lose all ability to understand or control what a given endpoint will return in any given situation > I have no specific error handling for the given problem then pass it on and decide what to do with it at the system boundary in the db: fun getUser(uuid: UUID) : Either<Error, User> middle layers: pass around the either, you can map, flatmap etc on it to chain it with other computations there then in the resource layer return user .fold({ error -> when(error) { is DbError -> HttpResponse.InternalServerError() is UserNotFoundError -> HttpResponse.NotFound } }, { user -> HttpResponse.ok(user) }) Then, at every layer, each method explicitly says in the method signature what it returns. There’s no need to look around each and every line of every method in every layer, or in the framework, or some global exception handler, to figure out what will happen. Developers can tell from a glance at the resource method what the endpoint will return for each outcome, in context. Things like being unable to communicate with the DB or not finding a user are not exceptional, they're entirely expectable when you're calling a db to look for a user and should be modeled accordingly.
- The_Colonel 3y ago> The problem with that is you lose all ability to understand or control what a given endpoint will return in any given situation Why? In the simplest (but pretty common) case it's: - successful response - generic error message > then pass it on and decide what to do with it at the system boundary Yes, but if in 99% of cases I only pass it on, it's just visual noise. Noise you become blind to, and it loses meaning. > Developers can tell from a glance at the resource method what the endpoint will return for each outcome, in context. They can't really because you end up with some very generic error type anyway. Any typical service can have IOError, DBConnectionError, SQLError, OutOfMemoryError, StackOverflowError plus many other application specific errors. You end up with bulk of your methods returning Either<Error, Something> which is then meaningless.
- bedobi 3y agoYou're describing the problems with exception, not using Either... When using Either, you never fold on those errors individually... Anyway, it's clear we're not going to agree, and you win, since so far the industry is still stubbornly clinging to exceptions, despite them being a failed feature in every language they're in.
- WirelessGigabit 3y agoIt feels like you're trying to use Exceptions as a way to steer your logic, otherwise why would you need to know why an operation failed to such detail? Your controller method cannot act differently on a DBConnectionerror or OutOfMemory error. Not to mention that exceptions cause developers to use them as control flow mechanisms. For example searching a user by id. If the database returns 0 records, is that reason to throw an exception? Well, doing result[0] results in IndexOutOfBounds due to result being []. But the reality is that the user not being there isn't exceptional. Typos are common. By using Result<T, E> or Either you enforce the developers to think more about their flow. One can write the method like this: fn find_by_id(id: usize) ->Result<UserResult, Error> -> { let raw_db_result = db.search("SELECT id, first_name, last_name FROM user WHERE id = ?", id)?; match raw_db_result { None => Ok(UserResult::NotFound), Some(r) => { let only_user = r.ensureOneResult()?; let user = mapDBUserToUser(only_user); Ok(UserResult::User(user)) } } } What about Error? Reality is that I don't really care. Error doesn't contain anything that is actionable. Either the whole chain succeeds and returns a valid result or the Error. The caller wants to find a user by id. There is one, or there isn't. All the rest is just errors that they'll pass on too. And in the end they get logged and result into Error 500. A 404 is actually a valid result. Now, if I were to use a throw new UserNotFoundException() for no user found you end up with generic try catches catching too much. And now someone needs to go and take it all apart to identify that single Exception that they want to deal with separately. Whereas if I want to add a state in my enum the callers _MUST_ update their code due to how Rust works.
- sam_lowry_ 3y agoEspecially because Kubernetes and the like work as if the application must die anyway. Pretty much like PHP CGI invocations always worked since 1998 or so.
- anon-3988 3y agoIn Rust you can just do "let value = result.unwrap()".