On Jun 23, 2019, at 19:57, MRAB
On 2019-06-24 02:43, Andrew Barnert wrote: On Jun 23, 2019, at 13:33, MRAB
wrote: Finally, under "For consideration: alternative syntaxes", my offering would be:
expr if condition1 and not condition2 else pass
This seems a lot more tenable than the original proposal. The “unless” seems both unnecessary and overly restrictive. Being able to specify “no argument/container element” in an expression is what the OP is really looking for, and spelling that “pass” makes sense, and then putting it inside the existing conditional expression gives the exact behavior the original example wanted without anything else new, ambiguous, or potentially confusing.
But I think it still needs to be fleshed out. I get the feeling you aren’t seriously proposing this change as something you want, but if the OP agrees that it gives him everything he wants, maybe he’ll be willing to think through all the details. And anyway, most of the same questions apply to the OP’s proposal, they’re just a bit harder to get at. Correct. Whilst I can see its point, I'm not calling for it, but merely suggesting alternative syntax that I think would be clearer. The big question is: if “pass” (or EMPTY) is allowed in (some) expressions, what happens if you use it somewhere illegal? For example, given than “1 if spam else pass” is a legal construct, is “return 1 if spam else pass” a SyntaxError (because that legal construct is something other than an expression), or is that a syntactically valid expression in a valid statement that just raises some kind of runtime error if the expression evaluates to “pass”, because return needs a value to return?
If it’s a syntax error, I think you really need to work through the language grammar and show what needs to change. My worry is that it would require duplicating half the nodes in the grammar (unless you literally can’t embed conditional-pass-expressions in any other construct except directly in an argument list or starred list—I assume you’d want to be able to put a conditional-pass inside parens, or inside another conditional-pass, if nothing else…), but I could easily be wrong.
If it’s a runtime error, the grammar seems a lot simpler—just make “pass” a special keyword identifier like None or False and I think you’re done (presumably remove the pass statement, too)—but the semantics are more complicated. Even forgetting about statements, what does it mean to yield an expression that evaluates to pass, or to lambda one?
Also, even within argument lists, container displays, and expression lists used for tuple values, I think there are some unclear things: can keyword-argument values, dict display keys and/or values, and starred and double-starred items be pass-valued, and, if so, what does that mean for each?
Meanwhile, although argument lists and container displays were the original desired use cases (and presumably also expression lists used as “tuple displays”), the OP actually wanted this to be used in “any statement expression”, and gave an assert statement as an example. I’m not quite sure what it’s supposed to do there (does it skip the assert? if so, how is that different from asserting True anyway?), and even less sure how to extend that idea to all statements that use an expression anywhere. Is this actually a necessary part of the feature? If so, there’s obviously even more work to be done defining the syntax and/or semantics.
Finally, it would be nice to see each example compared to the best way to do the same thing (presumably with iterable unpacking) in current Python, to see how much It actually improves readability. The only equivalents given by the OP are a case where you can just use an if statement (which looks better than the proposed new syntax—presumably the whole point of this is for cases where you can’t easily do that, as with arguments and list elements) and an expression that isn’t actually equivalent (if it were, this feature wouldn’t be needed at all).
As the "pass" indicates omission, it could really only be valid where an expression (possibly followed by a comma) can be omitted, which is as the top-level expression.
So that means you can’t combine a conditional pass expression into anything larger. You can’t even put it in parentheses. That already makes it seem a lot less usable, and makes it smell suspect as a concept too. Also, it feels very strange that “pass” can be used as the else-value of a conditional expression, but not in any other kind of expression. Also, at the statement level, you can already always trivially rewrite things, probably more readably, as an if statement. It’s only inside expression lists and expressions, where you can’t do so, that the feature seems at all compelling. Plus, I don’t think it really works.
For:
return expr if condition else pass
When condition is true:
return expr
When condition is false:
return
You’re getting two syntactically different statement forms, depending on the runtime value of condition. Of course that’s not impossible to implement, but it seems weird. But the real problem is that return is one of the only places in the language that actually works this way. Almost every other place you can have an expression in a statement, either the expression is mandatory (e.g., you can’t write x = with nothing on the right, so if that’s what x = 2 if cond else pass means when not cond, what? a SyntaxError at runtime?), or leaving it out doesn’t mean “use a sensible default value” but “do something different” (e.g., raise MyException(stuff) if cond else pass). Also, what about expression statements? Under your rule, there’s just no statement at all if not cond (possibly leading to an IndentError at runtime?). Meanwhile, this doesn’t work for the OP’s only statement example: assert spam if eggs else pass should not be the same as assert (a SyntaxError), or assert None (which raises an AssertionError because None is falsely), but do nothing at all. Which makes me think that maybe he’d expect your return statement to not return, rather than to return None.
What about "yield"? The same?
Well, yield isn’t a statement, it’s an expression, so that already falls under your “only the top-level expression in a statement” rule.
So, I can see a use for it only in places such as a tuple, list or set display, or a positional argument in an argument list.
The OP says it’s useful in assert. But I agree with you, all the potentially good uses seem to be when the expression is part of comma-separated list of expressions. Which I think means only container displays, expression lists (which usually work as “tuple displays”, but it’s a separate piece of syntax, and you have to be careful with the 0- and 1-element cases), and argument lists. But someone still needs to work out exactly what that means, and (if it’s going to be enforced syntactically rather than at runtime) what it does to the grammar. Because, again, I worry that the only way to allow, e.g., a starred_list to include pass elements is to fork half the nodes in the grammar underneath it.
Not sure about a dict display or a keyword argument; that might be going too far!
It seems to me that there’s no reason to ban starred items and keyword arguments if they fall naturally out of the change, but no reason to go out of the way to add them if they don’t. The semantics should be pretty obvious to any reader, but there’s also an existing simple way to write it (*pass clearly should do the same thing as *(), inserting zero positional arguments, right?). But dict displays, that could be confusing. Do you have to pass-value the key, or the value, or either of the two, or both consistently? If the key, does that short-circuit the value expression? So I think you’re right, that’s possibly worth banning even if you have to go out of your way to do so.