News for hackers. Stuff that matters.
Re:What's New in Python 3.11? by magicalhippo on Monday June 27, 2022 @05:07AM (#31892162)
> Exceptions are extremely confusing, I just can't wrap my head around them.

Interesting. For me it's very much the opposite. What about them do you find confusing?

> I don't even accept, philosophically speaking, the concept of exception: there are no "exceptions" when running a program [...]

I think you're looking at it wrong. Exceptions, as implemented in the languages I know, are more about having a framework of allowing inner/library code to drop the ball and run away screaming, without making more of a mess.

Re:What's New in Python 3.11? by usrbinbash on Monday June 27, 2022 @05:35AM (#31892361)
> are more about having a framework of allowing inner/library code to drop the ball and run away screaming, without making more of a mess.

It also allows the calling function to ignore the mess and hope that something further up the call-chain will deal with it.

Exceptions circumvent the normal control-flow of a program, and that makes code less obvious to follow. When I see foo calling bar() in go, I know where, and if, errors returned by the function are being handled, just by looking at foo.

When I see foo calling bar() in Python, and there is no try-except block in foo, I have no idea what happens to any errors that bar may return. I now have to check everyting that calls foo, which may be multiple other places, each of which could have a different Exception handling, or none, in which case I have to also check all the callers of that caller of foo, etc.

And even if there is a try-except, I have to check whether except catches the correct TYPE, so now I also need to be aware of the different types of Exceptions bar may raise ... which in turn depends on everything that bar calls, and how bar itself handles these calls, etc.

Yes, error handling in Go is verbose. Yes, it would be nice if it offeres a little syntactic sugar to improve that.

But error handling in Go is also obvious.

Re:What's New in Python 3.11? by joshuamorton on Monday June 27, 2022 @06:09AM (#31892554)
> And even if there is a try-except, I have to check whether except catches the correct TYPE, so now I also need to be aware of the different types of Exceptions bar may raise ... which in turn depends on everything that bar calls, and how bar itself handles these calls, etc.

Why? Keep in mind in go, you almost certainly don't check the type of the error. Why hold python to a hire standard (or the opposite: why don't you pass errors with types in golang, and handle errors of only a particular type or set of types?)

The answer, in both cases, is of course the same: errors are almost always handled in one of two cases: basically at the callsite (either at the callsite or in the parent function), or they are handled in "main" in some form of catchall.

Exceptions are really great for this. You very much don't worry about the things that might be thrown by a child of a child of a child of foo() that you call, because the abstraction shows that you shouldn't care (unless perhaps foo documents explicitly a particular function it throws). You don't need to waste code or mindshare on something you really don't care about. In go however, you do!

Re:What's New in Python 3.11? by usrbinbash on Monday June 27, 2022 @08:04AM (#31893352)
> Why?

Because unless the caller of foo uses a catchall, it may not actually catch the exception raised by bar. Lets say bar opens a file, and callerOfFoo says `except FileNotFoundError:` ... what if bar opens a file that exists with insufficient Permissions? Then it's `PermissionError`, and callerOfFoo won't catch it.

Sure, its possible that callerOfFoo is prepared for that, but my point is, I don't know that unless I check its code.

Re:What's New in Python 3.11? by joshuamorton on Monday June 27, 2022 @11:55AM (#31896344)
But again, why do you care?
Re:What's New in Python 3.11? by dementiapatien on Tuesday June 28, 2022 @07:37AM (#31906430)
Because I can visualize Go errors completely. They're simple links from one place to another. They are straight fibers connecting my modules and functions to each other. They always work exactly like I expect them to.

With python and other exception based languages? I have made hundreds of commits in my lifetime after being surprised that some random 3rd party library throws a nonstandard exception and breaks all sensible control flow that everybody assumed was happening.

Re:What's New in Python 3.11? by joshuamorton on Wednesday June 29, 2022 @04:43AM (#31917790)
This is the reverse of the issue that the parent mentioned though!

Consider a go function foo which returns some value and an error. What can you do with that error? You mention control flow being broken by python and others, but the control flow of

    def myfunc():
        if foo():
            do_a()
        else:
            do_b()
and

    def myfunc():
        try:
            cond = foo()
        except:
            raise
        
        if cond:
            do_a()
        else:
            do_b()
and

    func myfunc() err {
        cond, err := foo()
        if err != nil {
            return err
        }
        if cond {
            do_a()
        } else {
            do_b()
        }
Are all the same! And you can't do anything different in them, because in go, you have no knowledge about the error. Is it aa temp error you should retry? Who knows, are you going to parse the error string to figure it out? The only think you can do in go to an error from some library function is pass the error, possibly adding some additional context, because otherwise you may be mishandling the error.

In exception based languages the "pass the error" case is done for you, but if you do want to retry retriable errors, you can actually use a language feature to know that. In go, you have to hope the library creator created an interface (and that all of the possible errors that could percolate up have compatible error-extension interfaces!) that includes some kind of type information, which almost no one does.

You're talking about "sensible" control flow, but go doesn't have it!

Re:What's New in Python 3.11? by dementiapatien on Thursday June 30, 2022 @08:33AM (#31931583)
I argue that errors.Wrap and errors.Is from the Go stdlib solves this problem. Libraries can export their own error types and you can check if they threw that error type. I use this pattern all the time to handle special errors differently. Used it to implement optimistic locking which does ~not~ simply propagate all errors upwards!

https://gosamples.dev/check-error-type/

  cond, err := foo()
  if errors.Is(err, ErrFooHadTemporaryFailure) {
    // retry
  } else if errors.Is(err, ErrFooHostOffline) {
    // switchFooHost() 
  } else if err != nil {
    // propagate unhandled error upwards
    return nil, fmt.Errorf("foo unhandled err: %w", err)
  }

Of course it is up to the package author (or yourself) to write functions that return specific wrapped error types. Otherwise we're stuck in the situation your comment describes.

What do you mean by creating an error interface? It's a one liner to make a new error case:

  var ErrFooHadTemporaryFailure = errors.New("temporarily failure, retry later")
Re:What's New in Python 3.11? by usrbinbash on Thursday June 30, 2022 @12:05PM (#31934521)
I can define error values, even wrap them, and check against them, which also works on wrapped errors without having to unwrap them first:

https://go.dev/play/p/C_Yv5s6USma

Re:What's New in Python 3.11? by thombat on Monday June 27, 2022 @06:56AM (#31892806)
Please indulge an ignorant question from a Go-curious outsider: are ignored error returns flagged/grumbled-about? One of the enduring PITAs with C-error handling is that AFAIK there's no good way to highlight slipshod code. For example printf() returns "int number_chars_written", system() returns "int status_code": ignoring the first case is ubiquitous and having to cast it to (void) is noisy, ignoring the second is a bad smell and warrants at least a comment.

How does this compare to error handling in Go?

Re:What's New in Python 3.11? by usrbinbash on Monday June 27, 2022 @07:42AM (#31893164)
> are ignored error returns flagged/grumbled-about?

No, they are not.

    func couldFail() error { /*...*/ }
    func ohNoes() {
        couldFail()
    }
ohNoes() can call couldFail() and ignore the error return completely, without the compiler complaining.

This is intended. Error returns are not special; they are just another return value. Error handling by the caller includes "I don't care if there is an error". Yes, in some situations that's a bad code smell. In others, it really doesn't matter. Go puts the responsibility to decide which applies in the hands of the programmer.

To get the same effect in python, I'd have to wrap the call in

    try:
        couldFail()
    except:
        pass
Re:What's New in Python 3.11? by jenny91 on Monday June 27, 2022 @01:16PM (#31897623)
I'd hope it's a pretty non-controversial thing by now that software shouldn't silently fail without indicating something's wrong?

If I saw the latter Python code in a code review I would squirm. I would scrutinize it very seriously and if there's some really odd case where you really want this, I'd require at least a logger line or a comment to explain why in the world this is desired behavior.

Re:What's New in Python 3.11? by usrbinbash on Tuesday June 28, 2022 @04:15AM (#31905398)
> I'd hope it's a pretty non-controversial thing by now that software shouldn't silently fail without indicating something's wrong?

That depends whether the error condition is worth caring about, for a given application, in a given state.

eg. how often do programs check if `printf("something")` actually succeeded?

Re:What's New in Python 3.11? by jenny91 on Tuesday June 28, 2022 @11:19AM (#31908859)
> eg. how often do programs check if `printf("something")` actually succeeded?

Probably not very often? But they probably should?

If you're printing something, there's probably a case where you care about that being printed, and if it's not in that case, that's bad?

This also applies to logs too. Just because you're not always looking at the logs doesn't mean they're not important when you need them.

Sure, if you have some chatty logs going through a distributed logging system you might not want to crash the whole thing, and in that case you might want to ignore the exception. But this is a very fringe case.

Re:What's New in Python 3.11? by digisign on Monday June 27, 2022 @01:54PM (#31898151)
Exceptions propagate up the call stack until caught, simple as that.

I prefer this way, but there was one thing I didn't understand until Ned Batchelder taught me, how to layer them:

- https://nedbatchelder.com/text/exceptions-in-the-rainforest....

- https://nedbatchelder.com/text/exceptions-vs-status.html

I didn't have the full mental model until I read that.

Re:What's New in Python 3.11? by code_runner on Monday June 27, 2022 @06:42AM (#31892713)
Things can still panic in go.

Error handling for an API etc usually make the most sense in a central location (some middleware etc) so why would I check that each and every function call was successful.

I do like go, but the error handling is hardly the best thing about it. It’s just different. Neither is wrong.

Re:What's New in Python 3.11? by usrbinbash on Monday June 27, 2022 @07:50AM (#31893229)
> Things can still panic in go.

Yes, but panics are intended to represent an actually exceptional situation, like dereferencing nil, or the machine running out of memory ... things that normally shouldn't happen, and from which the logic cannot easily recover.

> Error handling for an API etc usually make the most sense in a central location (

Which is doable:

    if err := couldFail(); err != nil {
        return err
    }
I can let errors bubble up the call stack as far as I want, I just have to be explicit about it.
Re:What's New in Python 3.11? by enriquto on Monday June 27, 2022 @05:32AM (#31892350)
> What about them do you find confusing?

They are non-local jumps. They make reasoning about the code very difficult. When you read code, you have to treat all conditions symmetrically and equally likely (e.g., whether a file exists or it doesn't). Using exceptions for control flow, as is done sometimes in python, forces an unnatural asymmetry between cases that I find confusing. And this is just when there is a single exception at stake. Typically, several exceptions fly invisibly over the same code and it becomes impossible to understand (unless you assume that no exceptions occur, which is the wrong stance to take when analyzing an exception-riddled code).

TL;DR: raise and except are a corporate-friendly renaming of goto [0] and comefrom [1].

[0] https://en.wikipedia.org/wiki/Goto

[1] https://en.wikipedia.org/wiki/COMEFROM

Re:What's New in Python 3.11? by magicalhippo on Monday June 27, 2022 @05:54AM (#31892481)
> When you read code, you have to treat all conditions symmetrically and equally likely

Pretty sure I don't do that when I read code. When I see "list.add()" I don't consider running out of memory and the operation failing equally likely to the list being added to. And if it did, in 99.99% of the cases I'm fine with it just bailing, because there's not much else to do at that point.

I agree that using exceptions for what could be considered normal conditions is not great. Trying to open a file that doesn't exist isn't by itself an exceptional incident. The calling code might consider it an exception and decide to raise, but the IO library shouldn't.

Re:What's New in Python 3.11? by hnaccount_rng on Monday June 27, 2022 @06:22AM (#31892615)
> Trying to open a file that doesn't exist isn't by itself an exceptional incident. The calling code might consider it an exception and decide to raise, but the IO library shouldn't.

This is a way of thinking that I don't get. Yes it's fine that an IOLibrary has to be able to handle a FileDoesNotExistError condition. But from the caller POV: I clearly instructed you to open a file of some description. I expect you to return to me a handle for said file. Everything that does not match that expectation is exceptional (to the caller).

And it is that violation of expectations that is communicated by (Python's) exceptions.

Re:What's New in Python 3.11? by magicalhippo on Monday June 27, 2022 @06:36AM (#31892675)
> Everything that does not match that expectation is exceptional (to the caller).

No. If you want to read some data from a file if it exists, and continue merrily along if it doesn't, then you cannot simply check if the file exists and then try to open it. That would lead to a race condition. The file could for example be deleted in between the two calls.

The only proper way to handle that is to try to open the file, and if the result of that is FileDoesNotExistError then you continue along merrily.

If the file the caller tries to open is a settings override file, say, then it's not exceptional that the file does not exist.

Re:What's New in Python 3.11? by acoard on Monday June 27, 2022 @01:26PM (#31897771)
> Everything that does not match that expectation is exceptional (to the caller).

Sure, but saying "open this file" and having the response "nothing here, boss" is not exceptional. It's pretty normal. Isn't that just one of the two obvious options when you try and open a file? To me, exceptional would be something like "this file was open and now it's deleted."

Re:What's New in Python 3.11? by afiori on Tuesday June 28, 2022 @03:10AM (#31905017)
I am convinced that a mixed goto-comefrom model would work best; consider this pseudo-syntax:

    try {
      some_code();
    } catch (e) {
      var fixed = handle(e);
      if(fixed){
        continue; // goes back to where the error was trown
      } else {
        beak;// gives up and continues from here
      }
    }
Re:What's New in Python 3.11? by enriquto on Tuesday June 28, 2022 @04:15AM (#31905393)
In fact, if we want to go all-in with this style, we could remove the if/else construction altogether (from the language!) and do everything using exceptions. Since they are good for control flow, why stop here?

The old-fashioned

    if (condition):
            statement_1
    else:
            statement_2
now becomes

    try:
            assert(condition)
            statement_1
    except AssertionFailed:
            statement_2
This is much clearer, since the "normal" flow of execution is emphasized, and it avoids using an ad-hoc legacy if/else construct. Moreover, it has the advantage that statement_2 can be hidden into another part of your program, wherever you want to catch this exception.
Re:What's New in Python 3.11? by afiori on Tuesday June 28, 2022 @05:03AM (#31905651)
I was thinking more of making a parallel with effect handler system currently in development for Ocaml and other research languages, you do something similar to throwing a continuations[0][1][2].

It is proposed as an alternative to both the current monad-oriented approach used in Haskell and the procedural side-effects of OCaml.

To my understanding the base idea is that you can register "handlers" for (user defined) "effects" that can be emitted from normal code.

Once an effect is emitted the handler receives a continuation and can decide what to do with it (call it immediately, register it in a async/await pool, repeat it 5 times).

It would offer a type safe way to implement async/await as library code, which sounds quite cool.

The proposed try/catch/continue was a silly bastardization of this idea.

[0] https://en.wikipedia.org/wiki/Continuation

[1] https://www.youtube.com/watch?v=6lv_E-CjGzg

[2] https://www.youtube.com/watch?v=X30xmcOow2U

Re:What's New in Python 3.11? by code_runner on Monday June 27, 2022 @06:45AM (#31892732)
If people overuse try/catch, the code is close to impossible to read… but try/catch isn’t meant to be applied liberally throughout the code. Don’t let someone’s misunderstandings and bad code ruin it for you.

Typically you just care to have one or two primary places where you actually care if something has gone wrong.