[Python-ideas] PEP 572: Assignment Expressions (post #4)

Chris Angelico rosuav at gmail.com
Wed Apr 11 09:25:24 EDT 2018


On Wed, Apr 11, 2018 at 10:23 PM, Clint Hepner <clint.hepner at gmail.com> wrote:
>> On 2018 Apr 11 , at 1:32 a, Chris Angelico <rosuav at gmail.com> wrote:
>>    # Similar to the boolean 'or' but checking for None specifically
>>    x = "default" if (eggs := spam().ham) is None else eggs
>
>>
>>    # Even complex expressions can be built up piece by piece
>>    y = ((eggs := spam()), (cheese := eggs.method()), cheese[eggs])
>
> These would be clearer if you could remove the assignment from the expression itself.
> Assuming "let" were available as a keyword,
>
>     x = (let eggs = spam().ham
>          in
>          "default" if eggs is None else eggs)
>     y = (let eggs = spam(),
>              cheese = eggs.method()
>          in
>          (eggs, cheese, cheese[eggs]))
>
> Allowing for differences in how best to format such an expression, the final
> expression is clearly separate from its component assignment. (More on this
> in the Alternative Spellings section below.)

I have no idea what the "in" keyword is doing here, but somehow it
isn't being used for the meaning it currently has in Python. Does your
alternative require not one but *two* new keywords?

>> Differences from regular assignment statements
>> ----------------------------------------------
>>
>> An assignment statement can assign to multiple targets::
>>
>>    x = y = z = 0
>>
>> To do the same with assignment expressions, they must be parenthesized::
>>
>>    assert 0 == (x := (y := (z := 0)))
>
> There's no rationale given for why this must be parenthesized.
> If := were right-associative,
>
>     assert 0 == (x := y := z := 0)
>
> would work fine. (With high enough precedence, the remaining parentheses
> could be dropped, but one would probably keep them for clarity.)
> I think you need to spell out its associativity and precedence in more detail,
> and explain why the rationale for the choice made.

It's partly because of other confusing possibilities, such as its use
inside, or capturing, a lambda function. I'm okay with certain forms
requiring parens.

>> Augmented assignment is not supported in expression form::
>>
>>>>> x +:= 1
>>      File "<stdin>", line 1
>>        x +:= 1
>>            ^
>>    SyntaxError: invalid syntax
>
> There's no reason give for why this is invalid. I assume it's a combination
> of 1) Having both += and +:=/:+= would be redundant and 2) not wanting
> to add 11+ new operators to the language.

And 3) there's no point. Can you give an example of where you would
want an expression form of augmented assignment?

> 4. Adding a ``let`` expression to create local bindings
>
>     value = let x = spam(1, 4, 7, q) in x**2 + 2*x
>
> 5. Adding a ``where`` expression to create local bindings:
>
>     value = x**2 + 2*x where x = spam(1, 4, 7, q)
>
> Both have the extra-keyword problem. Multiple bindings are little harder
> to add than they would be with the ``where:`` modifier, although
> a few extra parentheses and judicious line breaks make it not so bad to
> allow a comma-separated list, as shown in my first example at the top of
> this reply.

Both also have the problem of "exactly how local ARE these bindings?",
and the 'let' example either requires two new keywords, or requires
repurposing 'in' to mean something completely different from its usual
'item in collection' boolean check. The 'where' example is broadly
similar to rejected alternative 3, except that you're removing the
colon and the suite, which means you can't create more than one
variable without figuring some way to parenthesize. Let's suppose this
were defined as:

EXPR where NAME = EXPR

as a five-component sequence. If you were to write this twice

EXPR where NAME = EXPR where OTHERNAME = EXPR

then it could just as logically be defined as "EXPR where NAME = (EXPR
where OTHERNAME = EXPR)" as the other way. And even if it were to work
as "(EXPR where NAME = EXPR) where OTHERNAME = EXPR", that still has
the highly confusing semantics of being evaluated right-to-left.

(Before you ask: no, you can't define it as "EXPR where NAME = EXPR ,
NAME = EXPR", because that would require looking a long way forward.)

>> Special-casing conditional statements
>> -------------------------------------
>>
> 4. `` let NAME = EXPR1  in EXPR2``::
>
>     stuff = [let y = f(x) in (y, x/y) for x in range(5)]
>
> I don't have anything new to say about this. It has the same keyword
> objections as similar proposals, and I think I've addressed the use case
> elsewhere.

This section is specifically about proposals that ONLY solve this
problem within list comprehensions. I don't think there's any point
mentioning your proposal there, as the "let NAME = EXPR in EXPR"
notation has nothing particularly to do with comprehensions.

>> With assignment expressions, why bother with assignment statements?
>> -------------------------------------------------------------------
>>
>> The two forms have different flexibilities.  The ``:=`` operator can be used
>> inside a larger expression; the ``=`` operator can be chained more
>> conveniently, and closely parallels the inline operations ``+=`` and friends.
>> The assignment statement is a clear declaration of intent: this value is to
>> be assigned to this target, and that's it.
>
> I don't find this convincing. I don't really see chained assignments often enough
> to worry about how they are written, plus note my earlier question about the
> precedence and associativity of :=.

If you don't use them, why would you care either way? :)

> The fact is, `x := 5` as an expression statement appears equivalent to the
> assignment statement `x = 5`, so I suspect people will start using it as such
> no matter how strongly you suggest they shouldn't.

Probably. But when they run into problems, the solution will be "use
an assignment statement, don't abuse the assignment expression". If
you want to, you can write this:

np = __import__("numpy")

But it's much better to use the import statement, and people will
rightly ask why you're using that expression form.

Your "let... in" syntax is kinda interesting, but has a number of
problems. Is that the exact syntax used in Haskell, and if so, does
Haskell use 'in' to mean anything else in other contexts?

ChrisA


More information about the Python-ideas mailing list