Hacker News new | ask | show | jobs
by jmholla 950 days ago
Why does the author want to swallow these exceptions? Let them propagate and it's obvious where the issue lies. If you can't handle an exception, don't catch it.

> It's impossible to know which line might throw an error without reading the functions themselves... and the functions those functions call... and the functions those functions call. Some thorough engineers may document thrown errors but documentation is untested and therefore untrustworthy. Java is a little better because it forces you to declare uncaught errors in method signatures.

The author's proposal doesn't change this as much as they think it does. You still don't know what type of errors a function will throw without inspecting the code and thus how to resolve them. Unless, you have a blanket switch for every possible error anything could return which is the very thing they are complaining about.

1 comments

Author here!

> Why does the author want to swallow these exceptions?

Sometimes you want to swallow exceptions and sometimes you don't. The examples in the article may be a little contrived, but there are situations where logging an error and continuing is better because it prevents data loss.

> Let them propagate and it's obvious where the issue lies.

If they propagate down the stack then you lose context. You also may not have all the data you need to properly recover (e.g. still write to a table but make the errored field null).

> You still don't know what type of errors a function will throw without inspecting the code and thus how to resolve them.

You're right that we don't know which error occurred with the approach in the article, but you at least know that there could be an error. This is better than try/catch because that doesn't tell you whether an error could happen

> Sometimes you want to swallow exceptions and sometimes you don't.

You capture `Exception' in your examples which is a serious anti-pattern in nearly all cases. One of the rare places you'd want to do that is at the top level of a service that has to clean up safely before bailing out. None of the contrivances in your example demonstrate that requirement.

If you need to capture an exception, you capture the narrowest one that matches the business/technical requirement you have. The rest should bubble up and get handled by another part of your code or, indeed, cause your app to crash if it's unhandled.

Exceptions don't mean "error" either; they're just exceptions.

> If they propagate down the stack then you lose context

I cannot think of any instances where you 'lose context' by propagating them. You should raise Foo from e if you're re-raising, though, which you clearly did not bother doing. That does preserve context.

Passing an instance of an Exception object around? What is this madness... are you building a debugger? No? A complex framework or tool like pytest where you may want to manipulate the stack frames to benefit the end user? No? Then don't pass exception objects around and raise them somewhere else.

> his is better than try/catch because that doesn't tell you whether an error could happen

It's really not that simple. Don't forget that the concept of exceptions (and more powerful things, like CL's condition system) were invented because of the problems encountered with errors-as-value approaches.

It's a fundamental trade off without a right answer, and people have been arguing about it now for 50+ years without resolution.

> If they propagate down the stack then you lose context.

Maybe I’m misunderstanding you, but that’s what the Python stack traceback is for. It works pretty well, I prefer it over JS.

> If they propagate down the stack then you lose context. You also may not have all the data you need to properly recover (e.g. still write to a table but make the errored field null).

Is this not solved by using raise from?

By "context" I mean other data that lived at the time of the error. For example, you may want to update a table row when there's an error but if a thrown error takes you too far down the stack then you might not know the row ID anymore
Then you have built your program incorrectly and not caught the error in a place where you still have context (e. g. `YourProgramException` sub-type that indicates a recoverable error or `Exception` in the case that you're building something where it doesn't matter what the error is, just that an error occurred).

This can also be done with error passing, and is a design failure there too:

    _, ex = perform_batch_operation(on: list_of_data)
    if ex:
        # Oh no, we don't know which entry in the list failed
        # and can't update the appropriate row
        # (This, of course, should be handled in
        # perform_batch_operation, not bubbled up here)
you would have that preserved as a variable in the context of the error no?