December 16, 2011

Nothing Stranger Than This?

I’ve just been reading Stack Overflow’s Strangest Language Feature thread, where I discover that (at the time of writing) the highest-voted Python complaint is actually about Java, but the complainant claims that JavaScript and Python are the same. So after I've got this off my chest I'll perhaps go back and look for the top complaint specifically about Python. In Java the complained-of code looks like this:

    try {
        return true;
    } finally {
        return false;

This naturally translates into

       return True
       return False

which, under every recent CPython I've tried (2.{6,7}.X and 3.X), returns False. What I can’t understand is why anyone would expect anything different. If the finally suite contains a return then it’s pretty apparent that the programmer of that function wanted the function to always return a specific value. If there had been code following the return statement would the complainers have expected that to be executed? I'm not sure I see the reason for the complaint at all. What would the complainers have this function return? What is the return statement supposed to do?

This gave rise to a further thought: does a return in a finally clause suppress any active exception? This is only one of a number of exception-handling issues I've been thinking about lately, kind of noodling around the corner cases. More later.


EOL (Eric O LEBIGOT) said...

"What I can’t understand is why anyone would expect anything different. If the finally suite contains a return then it’s pretty apparent that the programmer of that function wanted the function to always return a specific value."

While I understand your point, you could also argue that if the try clause contains a return, then it's pretty apparent that the programmer wants to return something if no problem happens. :) Thus, the part of the try…finally that you look leads to a different conclusion.

In addition, I would naively expect code that runs without raising any exception to be fully executed. Instead, a "return" inside a "try" clause apparently makes the interpreter jump to the "finally" clause. I would not have guessed this without your post. :) I don't recall seeing anything about this in the documentation; it would be nice to have a reference about this feature.

Nick Coghlan said...

Indeed, a return or break in a finally clause will both kill the exception and replace it with the specified control flow.

continue is simply disallowed, while yield will suspend the frame as normal, and then continue the exception processing once the generator is restarted.

Nick Coghlan said...

I'll note that I *did* have to double-check those at the interactive prompt. Only the continue behaviour surprised me though - I thought it behaved similarly to the way break and return do.

Steve said...

Maybe I just took the finally definition more literally than most. I'vr always assumed it would trump whatever came before it, and it seems I was right by chance or intuition or whatever.

subdigit said...

I guess to me it just violates the contract one would expect with a "return" statement. Now the coder has to visually scan the rest of the context to find out if there are any gotchas that would invalidate what one would normally believe to be a pretty deterministic clause to see.

Now this may indeed differ from how the compiler interprets (or how the language actually defines the finally action), but I t hink in terms of readability and implied contract, a return should really return and be final on its execution and not be trumped.

Steve said...

@subdigit: Well that's where we differ (and I don't want it to seem as though I am suggesting my own interpretation is “obvious” to anyone but me). I never thought that a return would be more final than a finally.

Also, let's not forget that we are talking about a function. You write as though the author of the try clause is somebody other than the author of the finally clause, which should not be the case in practice. I accept, though, that it might be confusing for someone coming along to maintain the function later. But really this code is simply pathological, an edge case whose behavior can be understood but isn't a great deal of practical use.

I toyed with the idea of writing a function whose code raised an exception at various places in order that a return statement in a finally clause can compute its return value from variables that have been set. But I couldn't honestly present that as something other than magical, so I decided not to bother. The trouble with writing code that serves as a bad example is that someone eventually comes along and uses it. Such programmers are a bit like people who don't understand sarcasm. Its difficult to be sarcastic with them.

Brendan Miller said...

I've got to say, C++'s destructors make a lot more sense than finally blocks.

In python the equivalent is the with statement I guess.

Even ignoring this problem, finally blocks are really awkward.