4 ms·
Python has the same construct but is removing it, starting with a warning in version 3.14: https://peps.python.org/pep-0765/ https://peps.python.org/pep-0765/
by ptx 11mo ago
Python has the same construct but is removing it, starting with a warning in version 3.14:
https://peps.python.org/pep-0765/ https://peps.python.org/pep-0765/
- teddyh 11mo agoThere was a recent talk at PYCON UK about it, by one of the authors of the PEP in question: <https://www.youtube.com/watch?v=vrVXgeD2fts https://www.youtube.com/watch?v=vrVXgeD2fts>
- ptx 11mo agoThat was a great talk. I was somewhat skeptical of this change, but she really looked at it from every angle and makes a strong argument for the benefits of disallowing the construct. Also, I didn't know that (as mentioned in the talk) C# already disallows it.
- maxdamantus 11mo agoInteresting .. from the post above: > The projects examined contained a total of 120,964,221 lines of Python code, and among them the script found 203 instances of control flow instructions in a finally block. Most were return, a handful were break, and none were continue. I don't really write a lot of Python, but I do write a lot of Java, and `continue` is the main control flow statement that makes sense to me within a finally block. I think it makes sense when implementing a generic transaction loop, something along the lines of: <T> T executeTransaction(Function<Transaction, T> fn) { for (int tries = 0;; tries++) { var tx = newTransaction(); try { return fn.apply(tx); } finally { if (!tx.commit()) { // TODO: potentially log number of tries, maybe include a backoff, maybe fail after a certain number continue; } } } } In these cases "swallowing" the exception is often intentional, since the exception could be due to some logic failing as a result of inconsistent reads, so the transaction should be retried. The alternative ways of writing this seem more awkward to me. Either you need to store the result (returned value or thrown exception) in one or two variables, or you need to duplicate the condition and the `continue;` behaviour. Having the retry logic within the `finally` block seems like the best way of denoting the intention to me, since the intention is to swallow the result, whether that was a return or a throw. If there are particular exceptions that should not be retried, these would need to be caught/rethrown and a boolean set to disable the condition in the `finally` block, though to me this still seems easier to reason about than the alternatives.
- AdieuToLogic 11mo ago> Having the retry logic within the `finally` block seems like the best way of denoting the intention to me, since the intention is to swallow the result, whether that was a return or a throw. Except that is not the documented intent of the `finally` construct: The finally block always executes when the try block exits. This ensures that the finally block is executed even if an unexpected exception occurs. But finally is useful for more than just exception handling — it allows the programmer to avoid having cleanup code accidentally bypassed by a return, continue, or break. Putting cleanup code in a finally block is always a good practice, even when no exceptions are anticipated.[0] Using `finally` for implementing retry logic can be done, as you have illustrated, but that does not mean it is "the best way of denoting the intention." One could argue this is a construct specific to Java (the language) and does not make sense outside of this particular language-specific idiom. Conceptually, "retries" are not "cleanup code." 0 - https://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html https://docs.oracle.com/javase/tutorial/essential/exceptions...
- maxdamantus 11mo agoSounds like the right intent to me. To pinpoint your existing quote from the documentation: > The finally block always executes when the try block exits. This ensures that the finally block is executed even if an unexpected exception occurs. The intent of the transaction code is that the consistency is checked (using `tx.commit()`) "even if an unexpected exception occurs". I'm not sure how else to interpret that to be honest. If you've got a clearer way of expressing this, feel free to explain.
- locknitpicker 11mo ago> The intent of the transaction code is that the consistency is checked (using `tx.commit()`) "even if an unexpected exception occurs". A transaction failing is the opposite of an unexpected event. Transactions failing is a central use case of any transaction. Therefore it should be handled explicitly instead of using exceptions. Exceptions are for unexpected events such as the node running out of memory, or a process failing to write to disk.