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

Clint Hepner clint.hepner at gmail.com
Wed Apr 11 10:46:58 EDT 2018


> On 2018 Apr 11 , at 9:25 a, Chris Angelico <rosuav at gmail.com> wrote:
> 
> 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?

Just one; I don't see using ``in`` here to be any more or a problem than was
reusing ``if``, ``then``, and ``else`` for conditional expressions.

Its purpose is to separate the bindings from the expression
they are used in; eggs and cheese are only valid in the expression that follows
"in".

Syntactically, this is pretty much identical to Haskell's "let" expression,
but the idea of an expression with local bindings is much older.
(https://en.wikipedia.org/wiki/Let_expression)

> 
>>> 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?

I wouldn't want one :). I'm just suggesting that the PEP include something to the
effect of "We're not adding augmented assignment expressions because...".

>> 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.

``in`` already has two different uses: as a Boolean operator (two, actually, with ``not in``)
and as part of the various ``for`` constructs. IMO, I don't see adding this third meaning
to be a problem.

With ``let``, the scope extends as far right of ``in` as possible:

    let NAME = EXPR in let OTHERNAME = EXPR in EXPR

is equivalent to

    let NAME = EXPR in (let OTHERNAME = EXPR in EXPR)

> 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.

I agree that `where` *should* be rejected; I don't really like them in Haskell, either.
I only listed ``let`` here because I assume it *will* be rejected due to its requiring
a new keyword, no matter how much I think adding a new keyword is warranted.

>>> 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.

Fair enough, although that suggests a proper let expression precludes
the need for any special handling.

> 
>>> 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? :)

I just mean this seems like a weak argument if you are trying to convince
someone to use assignment statements. "Assuming I never use chained assignments,
why should I use ``=`` instead of ``:=`?"

> 
>> 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.

All the dunder methods exist to support a higher-level syntax, and are not
really intended to be used directly, so I think a stronger argument than
"Don't use this, even though you could" would be preferable.

> 
> 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?


I don't believe ``in`` is used elsewhere in Haskell, although Python
already has at least two distinct uses as noted earlier.

In Haskell, the ``let`` expression (like much of Haskell's syntax) is
syntactic sugar for an application of lambda abstraction. The general form

    let n1 = e1
        n2 = e2
    in e3

is syntactic sugar for

    let n1 = e1
    in let n2 = e2
    in e3

where multiple bindings are expanded to a series of nested expressions. The single
expression

    let n1 = e1 in e2

itself is transformed into 

    (\n1 -> e2) e1

(or translated to Python, (lambda n1: e2)(e1)).

--
Clint


More information about the Python-ideas mailing list