
I recently(1 year ago) realized that 'if', 'elif', and 'else' provide easy branching for code, but there is no easy way to merge branches of code back together. One fix for this would be introduction of two new keywords: 'also' and 'alif' (also if) Here are the definitions of if-chain keywords with the addition of 'also' and 'alif': *if *- execute code if this condition is met *else *- execute code if no previous condition is met *elif *- execute code if no previous condition is met and this condition is met *also *- execute code if any previous condition is met *alif *- execute code if any previous condition is met and this condition is met This would simplify some logic expressions by allowing the merging of branched code. *Examples of use:* *Duplicate code in if-chains may be reduced:* # Old Version if a == b: print ('a == b') foo() # <-- duplicate code elif b == c: print ('b == c') foo() # <-- duplicate code elif c == d: print ('c == d') foo() # <-- duplicate code # New Version if a == b: print ('a == b') elif b == c: print ('b == c') elif c == d: print ('c == d') also: foo() # <-- No longer duplicated *Many nested 'if' statements could now be a more linear style:* # Old Version if a == b: print ('a == b') if b == c: print ('b == c') print ('end if') # New Version if a == b: print ('a == b') alif b == c: print ('b == c') also: print ('end if') These two examples are the most common ways this will help code. I have been writing code samples using these keywords and have found that it simplifies many other things as well. It does take a bit of getting used to, though. I have found that is is best to use 'also' and 'alif' sparingly, as overuse can make some code less flexible and more confusing. *Selective Branch merging:* One limitation of the 'also' and 'alif' keywords is the restriction to the "all of the above" checking. What I mean by that is that there is no way to pick and choose which branches to merge back together. When using 'also' and 'alif' you are catching all previous if-branches. One easy way to solve this would be to allow for named branching. The most simple way to do this is to save the conditions of each branch into a variable with a name. Here is an example of merging only select branches together: # Old Version if a == b: print ('a == b') elif a == c: print ('a == c') elif a == d: print ('a == d') if (a == b) or (a == d): print ('a == b and a == d') # New Version using 'as' keyword if a == b as aisb: print ('a == b') elif a == c: print ('a == c') elif a == d as aisd: print ('a == d') alif aisd or aisb: print ('a == b and a == d') NOTE: In the old version, it may be necessary to save off the boolean expression beforehand if the variables being used are changed in the first set of conditions. This would not be required in the new version, making the total code written an even larger gap. I realize that using the 'as' keyword may not be the best. Using 'as' seems to suggest that it will only be used in the following block. An alternative to using the 'as' keyword could be assigning the 'if' to a variable like so: aisb = if a == b: This looks a bit gross to me, though. If think of a better one, I would love to see it. *Speed:* The next logical question to ask is that of speed. Will this slow down my code at all? I happily submit to you that it shouldn't. If done right, this may even speed things up by a cycle or two (yeah. I know. That is not enough to fuss over, but I view it as a side benefit.) When just using 'also' and 'alif', there should be no difference in speed. The same number of jumps and checks should be done as before. Naming branches may add an additional assignment operation, but this should be more than made up for by not having to calculate the condition more than once. There may be a few cases where this would be slower, but those can be optimized to result in old-style code. *Implementation:* I am currently learning how the python parser and lexer work in hopes of making a custom version containing these features. Because of my lack of knowledge here, I cannot say how it should be implemented in python specifically. Here is how each if code block should work in basic english: on True: 1. Execute code block 2. Jump to next 'alif', 'also', or end of if-chain on False: 1. jump to next 'elif', 'else', or end of if-chain Note: 'also' blocks can be useful in more places than just the end of a chain: if a == b: print ('a == b') elif a == c: print ('a == c') also: print ('a == b == c') else: print ('a != b and a != c') It could also be useful to have more than one 'also' in an if chain. In contrast, having an 'else' before the end is not so useful. For example, placing one before an also makes the also a little pointless: if a == b: print ('a == b') elif a == c: print ('a == c') else: print ('a != b and a != c') also: print ('This will always execute') It would therefore be good to still require that 'else' be the last item in a chain. *The End* Thank you for humoring my idea. I am new to this mailing list, so sorry if this seems out of line or something. I am currently working with ansi C to try to get something similar into the 'C' language, but it could be 20 years before anything comes of that. The gears move ofly slow over there. Anyway, I look forward to hearing your feedback. -Cory Beutler

On Sat, Jun 6, 2015, at 23:03, Cory Beutler wrote:
Well you could always go with if aisb = a == b. I'm not sure there is a convincing reason to allow your case (assign within an if statement) that doesn't also work just as well as a general argument for assignment expressions.

On Sat, Jun 06, 2015 at 11:40:55PM -0400, random832@fastmail.us wrote:
Well you could always go with if aisb = a == b.
No, that is a terrible design and a source of many bugs in languages that allow it. if a = expr: ... Oops, I meant to compare a == expr, instead I assigned the result of the expression to a. I'm not convinced that we need to allow name binding in if/elif clauses, but if we do, the Pythonic syntax would be if a == b as aeqb: ... -- Steve

Steven D'Aprano <steve@pearwood.info> wrote: On Sat, Jun 06, 2015 at 11:40:55PM -0400, random832@fastmail.us wrote: >> Well you could always go with if aisb = a == b. > No, that is a terrible design and a source of many bugs in languages > that allow it. > if a = expr: ... > Oops, I meant to compare a == expr, instead I assigned the result of the > expression to a. In C I've mistyped this perhaps twice, in which case you get a compiler warning. It's a complete non-issue (and the construct *is* very handy). Stefan Krah _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/

On Jun 7, 2015, at 03:20, s.krah <stefan@bytereef.org> wrote:
In C I've mistyped this perhaps twice Then you must be an amazing programmer. Or maybe you don't code in C very much. Look through the commit history of any major early C project and you'll find plenty of these errors. Dennis Ritchie made this mistake more than two times just in the Unix source. Why do you think compilers added the warning? If this really were a non-issue that nobody ever faces in real life, no compiler vendor would have bothered to write a warning that will annoy people far more often than it helps. Or, if someone did just to satisfy some rare clumsy user, nobody else would have copied it. in which case you get a compiler warning. Of course you also get the compiler warning when you use this feature _intentionally_, which means it's actually not usable syntax (unless you like to ignore warnings from the compiler, or pepper your code with pragmas). Most compilers let you use some variant on the syntax, typically throwing meaningless extra parentheses around the assignment, to make the warning go away. But this implies that C chose the wrong syntax in the first place. If I were designing a new C-like language, I'd allow declarations, but not assignments, in the if condition (with the variable only live inside the if statement's scope). That would handle what you want 90% of the time, and usually better than the current rule, and would have no chance of confusing an assignment with a comparison, so the compiler warning would go away. But of course this is irrelevant to Python, which doesn't have variable declarations (or sub-function scopes). In Python, I think not allowing assignment in an if condition was the right choice.

On Sun, Jun 7, 2015 at 8:20 PM, s.krah <stefan@bytereef.org> wrote:
That's as may be, but Steven's still correct that the Pythonic way to do it would be with "as". In C, assignment is an expression ("=" is an operator that mutates its LHS and yields a value), but in Python, it simply isn't, and making it possible to do assignment in an 'if' condition would be a big change. with expr as name: except expr as name: if expr as name: Three parallel ways to do something and capture it. It makes reasonable sense, if someone can come up with a really compelling use-case. Personally, I'd be more inclined to seek the same thing for a while loop: while get_next_value() as value: # equivalent to while True: value = get_next_value() if not value: break as that's a somewhat more common idiom; but neither is hugely common. ChrisA

Andrew Barnert abarnert at yahoo.com wrote: >> In C I've mistyped this perhaps twice > Then you must be an amazing programmer. Or maybe you don't code in C very much. Or maybe I don't pontificate on mailing lists all day long. > Look through the commit history of any major early C project and you'll find plenty of these errors. Look through the commit history of CPython ... Stefan Krah

On 7 June 2015 at 22:55, s.krah <stefan@bytereef.org> wrote:
No need for that, folks. (And Stefan, you're definitely on the first half of Andrew's either/or statement there - not everyone is going to be aware that you wrote cdecimal, redesigned the memoryview implementation, etc). The C/C++ embedded assignment construct *is* a major source of bugs unless you really know what you're doing, which is why the compiler developers eventually relented and introduced a warning for it. It's a construct that trades clarity for brevity, so using it isn't always a clear win from a maintainability perspective. It's also heavily reliant on C's behaviour where assignment expression have a truth value that matches that or the RHS of the assignment expression, and the resulting longstanding conventions that have built up to take advantage of that fact. In Python, we've never had those conventions, so the alignment between "truth value you want to test" and "value you want to work with" isn't anywhere near as strong. In particular, testing for None via bool() is considered incorrect, while testing for a NULL pointer in C++ with a truth test is far more common. To get the same kind of utility as C/C++ embedded assignments provide, you really do need arbitrary embedded assignments. Only being able to name the result of an if or while clause would be too limiting to be useful, since you'd still need to find other ways to handle the cases where the value to be tested and the value you want to work with aren't quite the same. That's why this particular idea is oft-discussed-but-never-accepted - there are certain looping and conditional constructs that *do* become more convoluted in its absence, but the benefit of leaving it out is that those more convoluted constructs are needed to handle the general case anyway, so the special case would be an additional thing to learn that wouldn't actually make the language substantially more expressive than it already is. Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Chris Angelico<rosuav@gmail.com> wrote: >>> Oops, I meant to compare a == expr, instead I assigned the result of the >>> expression to a. >> >> In C I've mistyped this perhaps twice, in which case you get a compiler >> warning. >> >> It's a complete non-issue (and the construct *is* very handy). > That's as may be, but Steven's still correct that the Pythonic way to > do it would be with "as". I agree. I was mainly responding to the claim that it's a "major source of bugs" in languages that allow it. Stefan Krah

On Sun, Jun 7, 2015, at 08:20, Chris Angelico wrote:
The problem is, "with" and "except" create a variable whose scope is limited to the enclosed block. The proposed "if... as..." does not, so it's misleading. If we don't like spelling it as = then invent a new operator, maybe := or something. Maybe even as. My wider point, though, was that there's no argument for the _functionality_ of allowing an assignment of the boolean condition of an if statement that can't be generalized to allowing inline assignment of anything (why not e.g. "if (expr as foo) > 5"? That, unlike the boolean, might even be something that would be useful within the enclosed block.)

On Mon, Jun 8, 2015 at 1:04 AM, <random832@fastmail.us> wrote:
Actually, they don't. A with block tends to create a broad expectation that the object will be used within that block, but it isn't scoped, and sometimes there's "one last use" of something outside of the block - for instance, a Timer context manager which uses __enter__ to start timing, __exit__ to stop timing, and then has attributes "wall" and "cpu" to tell you how much wall time and CPU time were used during that block. There are a lot of context managers that might as well have disappeared at the end of the with block (open files, psycopg2 cursors (but not connections), thread locks, etc), but they are technically still around. The "except" case is a slightly different one. Yes, the name is valid only within that block - but it's not a matter of scope, it's a deliberate unsetting.
There's no concept of nested/limited scope here, although I'm sure that this particular case could be turned into a subscope without breaking anyone's code (I honestly cannot imagine there being ANY code that depends on the name getting unset!), if Python ever grows support for subscopes that aren't associated with nested functions. Comprehensions do actually create their own scopes, but that's actually implemented with a function:
Hence, the most logical way to handle conditions with 'as' clauses is to have them in the same scope. The special case for exceptions is because tracebacks would create refloops with the locals, and catching exceptions is extremely common. Nothing else needs that special case, so everything else can follow the 'with' block model and leave the name bound. ChrisA

On 8 June 2015 at 01:29, Chris Angelico <rosuav@gmail.com> wrote:
The unsetting of bound exceptions is also merely a language quirk introduced to cope with the implicit exception chaining introduced in PEP 3134. The circular reference from the traceback frame back to the bound exception caused a lot of uncollectable cycles that were resolved by automatically dropping the frame's reference to the bound exception. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Mon, Jun 8, 2015 at 1:54 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
Right. I know the reason for it, and it's a special case for exceptions because of the traceback. (Though I'm not sure why exception chaining causes this. Was that the first place where the traceback - with its reference to locals - was made a part of the exception object?) If Python had a concept of nested scopes within functions, it'd make equal sense to have the "except X as e:" subscope shadow, rather than overwriting and unsetting, the outer "e". Since neither that nor the list comprehension is implemented with nested scopes, I think it's safe to say that "if cond as e:" wouldn't be either. ChrisA

Cory Beutler <cgbeutler@gmail.com> writes:
This would simplify some logic expressions by allowing the merging of branched code.
I don't think you've made the case for that assertion. Your description is clear, but I can't see how real code would be simplified. I would like to see some real examples of code that is using existing syntax, and your proposed syntax, so the merits can be discussed in context. Can you provide some real-world code examples, that you believe would be improved by this change? -- \ “All my life I've had one dream: to achieve my many goals.” | `\ —Homer, _The Simpsons_ | _o__) | Ben Finney

On Jun 6, 2015, at 20:03, Cory Beutler <cgbeutler@gmail.com> wrote:
I recently(1 year ago) realized that 'if', 'elif', and 'else' provide easy branching for code, but there is no easy way to merge branches of code back together. One fix for this would be introduction of two new keywords: 'also' and 'alif' (also if)
Can you provide a realistic use case for when you'd want this, instead of just a toy example where you check meaningless variables for equality? Because in practice, almost every time I've wanted complicated elif chains, deeply nested ifs, or anything else like it, it's been easy to either refactor the code into a function, replace the conditionals with a dict, or both. That isn't _always_ true, but I'm having a hard time coming up with an example where I really do need complicated elif chains and your new syntax would help.
This example seems to do the wrong thing. If a=b=2 and c=3, or a=c=3 and b=2, you're going to print "a == b == c" even though that isn't true. This implies that maybe it isn't as easy to think through the logic and keep the conditions in your head as you expected, even in relatively simple cases.

On Sat, Jun 06, 2015 at 09:03:38PM -0600, Cory Beutler wrote: [...]
if a == b: print('a == b') elif b == c: print('b == c') elif c == d: print('c == d') foo() No new syntax required.
What's wrong with that code? Nesting the code like that follows the logic of the code: the b==c test *only* occurs if a==b.
I consider this significantly worse. It isn't clear that the comparison between b and c is only made if a == b, otherwise it is entirely skipped.
You don't say. Are you aware of any languages with this construct? What are the rules for combining various if...elif...alif...also...else blocks?
That code is wrong. Was that an intentional error? The final branch prints that a == b == d, but that's not correct, it runs when either a == b or a == d, not just when both are true. Personally, I would write that as: if a == b or a == d: if a == b: print('a == b') else: print('a == d') print('a == b or a == d') elif a == c: print('a == c') You do end up comparing a and b for equality twice, but worrying about that is likely to be premature optimization. It isn't worth adding syntax to the language just for the one time in a million that actually matters.
With this "as" proposal, there's no need for "alif": if a == b as aisb: print('a == b') elif a == c: print('a == c') elif a == d as aisd: print('a == d') if aisd or aisb: print('a == b or a == d') I think this has been proposed before. [...]
I realize that using the 'as' keyword may not be the best. Using 'as' seems to suggest that it will only be used in the following block.
Not necessarily. Consider "import module as spam". [...]
One advantage of Python is that the language does evolve more quickly, but still, Python is a fairly conservative language. We don't typically add new syntax features unless they solve a problem in an elegant fashion that cannot be solved easily without it. -- Steve

On Sat, Jun 6, 2015 at 8:03 PM, Cory Beutler <cgbeutler@gmail.com> wrote:
Seeing many posts on this list which are repeats of ideas seen many times, it's nice to see a new idea. I think the difficulty of making this work is how often you want something only when *all* of the previous conditions are true yet can't conveniently do it another way (e.g., setting a flag). Your point about writing conditions multiple time is legitimate and happens frequently. Here's an example of something where there is a similar difficulty in writing simple code: if foo.a == 0: ... elif foo.a == 1 and foo.b == 0: ... elif foo.a >= 1 and foo.b >= 0 and foo.c = 0: ... elif ... This is a generic example but I've written code like this many times and there is no simple way to say that all the foo.x values don't need to be computed more than once. Here it is rewritten to avoid recomputation: foo_a = foo.a if foo_a == 0: ... else: foo_b = foo.b if foo_a == 1 and foo_b == 0: ... else: foo_c = foo.c if foo_a >= 1 and foo_b >= 0 and foo_c = 0: ... else: ... Much harder to follow the logic. A simpler example where the same recomputation happens is: x = a and a.b and a.b.c and a.b.c.d which becomes x = a and a.b if x: x = x.c if x: x = x.d Ick. --- Bruce Check out my new puzzle book: http://J.mp/ingToConclusions Get it free here: http://J.mp/ingToConclusionsFree (available on iOS)

On 7 June 2015 at 15:50, Bruce Leban <bruce@leban.us> wrote:
This is one of the key powers of JIT compilers like PyPy and Numba - they can detect that a calculation is repeated and avoid repeating it when the compiler knows the input values haven't changed. There is no way any syntax addition can compete for clarity with using existing already clear syntax and speeding its execution up implicitly.
It's hard to reason about whether or not logic is difficult to follow when using metasyntactic variables, as they're never self-documenting. There's also the fact that this *specific* example is why having expensive-to-calculate values accessed as attributes without some form of caching is a bad idea - it encourages folks to make their code harder to read too early in the development process. Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Cory Beutler schrieb am 07.06.2015 um 05:03:
I think this is best done by extracting it into a function, e.g. def good_name_needed(): if a == b: print('a == b') elif b == c: print('b == c') elif c == d: print('c == d') else: # nothing to do here return foo() # <-- No longer duplicated Usually, in real code, the call to foo() will have some kind of relation to the previous if-chain anyway, so a good name for the whole function shouldn't be all to difficult to find. Stefan

On Sat, Jun 6, 2015, at 23:03, Cory Beutler wrote:
Well you could always go with if aisb = a == b. I'm not sure there is a convincing reason to allow your case (assign within an if statement) that doesn't also work just as well as a general argument for assignment expressions.

On Sat, Jun 06, 2015 at 11:40:55PM -0400, random832@fastmail.us wrote:
Well you could always go with if aisb = a == b.
No, that is a terrible design and a source of many bugs in languages that allow it. if a = expr: ... Oops, I meant to compare a == expr, instead I assigned the result of the expression to a. I'm not convinced that we need to allow name binding in if/elif clauses, but if we do, the Pythonic syntax would be if a == b as aeqb: ... -- Steve

Steven D'Aprano <steve@pearwood.info> wrote: On Sat, Jun 06, 2015 at 11:40:55PM -0400, random832@fastmail.us wrote: >> Well you could always go with if aisb = a == b. > No, that is a terrible design and a source of many bugs in languages > that allow it. > if a = expr: ... > Oops, I meant to compare a == expr, instead I assigned the result of the > expression to a. In C I've mistyped this perhaps twice, in which case you get a compiler warning. It's a complete non-issue (and the construct *is* very handy). Stefan Krah _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/

On Jun 7, 2015, at 03:20, s.krah <stefan@bytereef.org> wrote:
In C I've mistyped this perhaps twice Then you must be an amazing programmer. Or maybe you don't code in C very much. Look through the commit history of any major early C project and you'll find plenty of these errors. Dennis Ritchie made this mistake more than two times just in the Unix source. Why do you think compilers added the warning? If this really were a non-issue that nobody ever faces in real life, no compiler vendor would have bothered to write a warning that will annoy people far more often than it helps. Or, if someone did just to satisfy some rare clumsy user, nobody else would have copied it. in which case you get a compiler warning. Of course you also get the compiler warning when you use this feature _intentionally_, which means it's actually not usable syntax (unless you like to ignore warnings from the compiler, or pepper your code with pragmas). Most compilers let you use some variant on the syntax, typically throwing meaningless extra parentheses around the assignment, to make the warning go away. But this implies that C chose the wrong syntax in the first place. If I were designing a new C-like language, I'd allow declarations, but not assignments, in the if condition (with the variable only live inside the if statement's scope). That would handle what you want 90% of the time, and usually better than the current rule, and would have no chance of confusing an assignment with a comparison, so the compiler warning would go away. But of course this is irrelevant to Python, which doesn't have variable declarations (or sub-function scopes). In Python, I think not allowing assignment in an if condition was the right choice.

On Sun, Jun 7, 2015 at 8:20 PM, s.krah <stefan@bytereef.org> wrote:
That's as may be, but Steven's still correct that the Pythonic way to do it would be with "as". In C, assignment is an expression ("=" is an operator that mutates its LHS and yields a value), but in Python, it simply isn't, and making it possible to do assignment in an 'if' condition would be a big change. with expr as name: except expr as name: if expr as name: Three parallel ways to do something and capture it. It makes reasonable sense, if someone can come up with a really compelling use-case. Personally, I'd be more inclined to seek the same thing for a while loop: while get_next_value() as value: # equivalent to while True: value = get_next_value() if not value: break as that's a somewhat more common idiom; but neither is hugely common. ChrisA

Andrew Barnert abarnert at yahoo.com wrote: >> In C I've mistyped this perhaps twice > Then you must be an amazing programmer. Or maybe you don't code in C very much. Or maybe I don't pontificate on mailing lists all day long. > Look through the commit history of any major early C project and you'll find plenty of these errors. Look through the commit history of CPython ... Stefan Krah

On 7 June 2015 at 22:55, s.krah <stefan@bytereef.org> wrote:
No need for that, folks. (And Stefan, you're definitely on the first half of Andrew's either/or statement there - not everyone is going to be aware that you wrote cdecimal, redesigned the memoryview implementation, etc). The C/C++ embedded assignment construct *is* a major source of bugs unless you really know what you're doing, which is why the compiler developers eventually relented and introduced a warning for it. It's a construct that trades clarity for brevity, so using it isn't always a clear win from a maintainability perspective. It's also heavily reliant on C's behaviour where assignment expression have a truth value that matches that or the RHS of the assignment expression, and the resulting longstanding conventions that have built up to take advantage of that fact. In Python, we've never had those conventions, so the alignment between "truth value you want to test" and "value you want to work with" isn't anywhere near as strong. In particular, testing for None via bool() is considered incorrect, while testing for a NULL pointer in C++ with a truth test is far more common. To get the same kind of utility as C/C++ embedded assignments provide, you really do need arbitrary embedded assignments. Only being able to name the result of an if or while clause would be too limiting to be useful, since you'd still need to find other ways to handle the cases where the value to be tested and the value you want to work with aren't quite the same. That's why this particular idea is oft-discussed-but-never-accepted - there are certain looping and conditional constructs that *do* become more convoluted in its absence, but the benefit of leaving it out is that those more convoluted constructs are needed to handle the general case anyway, so the special case would be an additional thing to learn that wouldn't actually make the language substantially more expressive than it already is. Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Chris Angelico<rosuav@gmail.com> wrote: >>> Oops, I meant to compare a == expr, instead I assigned the result of the >>> expression to a. >> >> In C I've mistyped this perhaps twice, in which case you get a compiler >> warning. >> >> It's a complete non-issue (and the construct *is* very handy). > That's as may be, but Steven's still correct that the Pythonic way to > do it would be with "as". I agree. I was mainly responding to the claim that it's a "major source of bugs" in languages that allow it. Stefan Krah

On Sun, Jun 7, 2015, at 08:20, Chris Angelico wrote:
The problem is, "with" and "except" create a variable whose scope is limited to the enclosed block. The proposed "if... as..." does not, so it's misleading. If we don't like spelling it as = then invent a new operator, maybe := or something. Maybe even as. My wider point, though, was that there's no argument for the _functionality_ of allowing an assignment of the boolean condition of an if statement that can't be generalized to allowing inline assignment of anything (why not e.g. "if (expr as foo) > 5"? That, unlike the boolean, might even be something that would be useful within the enclosed block.)

On Mon, Jun 8, 2015 at 1:04 AM, <random832@fastmail.us> wrote:
Actually, they don't. A with block tends to create a broad expectation that the object will be used within that block, but it isn't scoped, and sometimes there's "one last use" of something outside of the block - for instance, a Timer context manager which uses __enter__ to start timing, __exit__ to stop timing, and then has attributes "wall" and "cpu" to tell you how much wall time and CPU time were used during that block. There are a lot of context managers that might as well have disappeared at the end of the with block (open files, psycopg2 cursors (but not connections), thread locks, etc), but they are technically still around. The "except" case is a slightly different one. Yes, the name is valid only within that block - but it's not a matter of scope, it's a deliberate unsetting.
There's no concept of nested/limited scope here, although I'm sure that this particular case could be turned into a subscope without breaking anyone's code (I honestly cannot imagine there being ANY code that depends on the name getting unset!), if Python ever grows support for subscopes that aren't associated with nested functions. Comprehensions do actually create their own scopes, but that's actually implemented with a function:
Hence, the most logical way to handle conditions with 'as' clauses is to have them in the same scope. The special case for exceptions is because tracebacks would create refloops with the locals, and catching exceptions is extremely common. Nothing else needs that special case, so everything else can follow the 'with' block model and leave the name bound. ChrisA

On 8 June 2015 at 01:29, Chris Angelico <rosuav@gmail.com> wrote:
The unsetting of bound exceptions is also merely a language quirk introduced to cope with the implicit exception chaining introduced in PEP 3134. The circular reference from the traceback frame back to the bound exception caused a lot of uncollectable cycles that were resolved by automatically dropping the frame's reference to the bound exception. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Mon, Jun 8, 2015 at 1:54 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
Right. I know the reason for it, and it's a special case for exceptions because of the traceback. (Though I'm not sure why exception chaining causes this. Was that the first place where the traceback - with its reference to locals - was made a part of the exception object?) If Python had a concept of nested scopes within functions, it'd make equal sense to have the "except X as e:" subscope shadow, rather than overwriting and unsetting, the outer "e". Since neither that nor the list comprehension is implemented with nested scopes, I think it's safe to say that "if cond as e:" wouldn't be either. ChrisA

Cory Beutler <cgbeutler@gmail.com> writes:
This would simplify some logic expressions by allowing the merging of branched code.
I don't think you've made the case for that assertion. Your description is clear, but I can't see how real code would be simplified. I would like to see some real examples of code that is using existing syntax, and your proposed syntax, so the merits can be discussed in context. Can you provide some real-world code examples, that you believe would be improved by this change? -- \ “All my life I've had one dream: to achieve my many goals.” | `\ —Homer, _The Simpsons_ | _o__) | Ben Finney

On Jun 6, 2015, at 20:03, Cory Beutler <cgbeutler@gmail.com> wrote:
I recently(1 year ago) realized that 'if', 'elif', and 'else' provide easy branching for code, but there is no easy way to merge branches of code back together. One fix for this would be introduction of two new keywords: 'also' and 'alif' (also if)
Can you provide a realistic use case for when you'd want this, instead of just a toy example where you check meaningless variables for equality? Because in practice, almost every time I've wanted complicated elif chains, deeply nested ifs, or anything else like it, it's been easy to either refactor the code into a function, replace the conditionals with a dict, or both. That isn't _always_ true, but I'm having a hard time coming up with an example where I really do need complicated elif chains and your new syntax would help.
This example seems to do the wrong thing. If a=b=2 and c=3, or a=c=3 and b=2, you're going to print "a == b == c" even though that isn't true. This implies that maybe it isn't as easy to think through the logic and keep the conditions in your head as you expected, even in relatively simple cases.

On Sat, Jun 06, 2015 at 09:03:38PM -0600, Cory Beutler wrote: [...]
if a == b: print('a == b') elif b == c: print('b == c') elif c == d: print('c == d') foo() No new syntax required.
What's wrong with that code? Nesting the code like that follows the logic of the code: the b==c test *only* occurs if a==b.
I consider this significantly worse. It isn't clear that the comparison between b and c is only made if a == b, otherwise it is entirely skipped.
You don't say. Are you aware of any languages with this construct? What are the rules for combining various if...elif...alif...also...else blocks?
That code is wrong. Was that an intentional error? The final branch prints that a == b == d, but that's not correct, it runs when either a == b or a == d, not just when both are true. Personally, I would write that as: if a == b or a == d: if a == b: print('a == b') else: print('a == d') print('a == b or a == d') elif a == c: print('a == c') You do end up comparing a and b for equality twice, but worrying about that is likely to be premature optimization. It isn't worth adding syntax to the language just for the one time in a million that actually matters.
With this "as" proposal, there's no need for "alif": if a == b as aisb: print('a == b') elif a == c: print('a == c') elif a == d as aisd: print('a == d') if aisd or aisb: print('a == b or a == d') I think this has been proposed before. [...]
I realize that using the 'as' keyword may not be the best. Using 'as' seems to suggest that it will only be used in the following block.
Not necessarily. Consider "import module as spam". [...]
One advantage of Python is that the language does evolve more quickly, but still, Python is a fairly conservative language. We don't typically add new syntax features unless they solve a problem in an elegant fashion that cannot be solved easily without it. -- Steve

On Sat, Jun 6, 2015 at 8:03 PM, Cory Beutler <cgbeutler@gmail.com> wrote:
Seeing many posts on this list which are repeats of ideas seen many times, it's nice to see a new idea. I think the difficulty of making this work is how often you want something only when *all* of the previous conditions are true yet can't conveniently do it another way (e.g., setting a flag). Your point about writing conditions multiple time is legitimate and happens frequently. Here's an example of something where there is a similar difficulty in writing simple code: if foo.a == 0: ... elif foo.a == 1 and foo.b == 0: ... elif foo.a >= 1 and foo.b >= 0 and foo.c = 0: ... elif ... This is a generic example but I've written code like this many times and there is no simple way to say that all the foo.x values don't need to be computed more than once. Here it is rewritten to avoid recomputation: foo_a = foo.a if foo_a == 0: ... else: foo_b = foo.b if foo_a == 1 and foo_b == 0: ... else: foo_c = foo.c if foo_a >= 1 and foo_b >= 0 and foo_c = 0: ... else: ... Much harder to follow the logic. A simpler example where the same recomputation happens is: x = a and a.b and a.b.c and a.b.c.d which becomes x = a and a.b if x: x = x.c if x: x = x.d Ick. --- Bruce Check out my new puzzle book: http://J.mp/ingToConclusions Get it free here: http://J.mp/ingToConclusionsFree (available on iOS)

On 7 June 2015 at 15:50, Bruce Leban <bruce@leban.us> wrote:
This is one of the key powers of JIT compilers like PyPy and Numba - they can detect that a calculation is repeated and avoid repeating it when the compiler knows the input values haven't changed. There is no way any syntax addition can compete for clarity with using existing already clear syntax and speeding its execution up implicitly.
It's hard to reason about whether or not logic is difficult to follow when using metasyntactic variables, as they're never self-documenting. There's also the fact that this *specific* example is why having expensive-to-calculate values accessed as attributes without some form of caching is a bad idea - it encourages folks to make their code harder to read too early in the development process. Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Cory Beutler schrieb am 07.06.2015 um 05:03:
I think this is best done by extracting it into a function, e.g. def good_name_needed(): if a == b: print('a == b') elif b == c: print('b == c') elif c == d: print('c == d') else: # nothing to do here return foo() # <-- No longer duplicated Usually, in real code, the call to foo() will have some kind of relation to the previous if-chain anyway, so a good name for the whole function shouldn't be all to difficult to find. Stefan
participants (10)
-
Andrew Barnert
-
Ben Finney
-
Bruce Leban
-
Chris Angelico
-
Cory Beutler
-
Nick Coghlan
-
random832@fastmail.us
-
s.krah
-
Stefan Behnel
-
Steven D'Aprano