[Python-ideas] PEP 505 (None coalescing operators) thoughts

Andrew Barnert abarnert at yahoo.com
Mon Sep 28 21:47:13 CEST 2015

On Monday, September 28, 2015 12:05 PM, Guido van Rossum <guido at python.org> wrote:
>On Mon, Sep 28, 2015 at 10:38 AM, Carl Meyer <carl at oddbird.net> wrote:
>>"Propagating" refers to the proposed behavior where use of ?. or ?[
>>"propagates" through the following chain of operations. For example:
>>    x = foo?.bar.spam.eggs
>>Where both `.spam` and `.eggs` would behave like `?.spam` and `?.eggs`
>>(propagating None rather than raising AttributeError), simply because a
>>`.?` had occurred earlier in the chain. So the above behaves differently
>>    temp = foo?.bar
>>    x = temp.spam.eggs
>>Which raises questions about whether the propagation escapes
>>parentheses, too:
>>    x = (foo?.bar).spam.eggs
>Oh, I see. That's evil.
>The correct behavior here is that "foo?.bar.spam.eggs" should mean the same as
>    (None if foo is None else foo.bar.spam.eggs)
>(Stop until you understand that is *not* the same as either of the alternatives you describe.)
>I can see the confusion that led to the idea of "propagation" -- it probably comes from an attempt to define "foo?.bar" without reference to the context (in this case the relevant context is that it's followed by ".spam.eggs").

It would really help to have a complete spec, or at least a quick workthrough of how an expression gets parsed and compiled.

I assume it's something like this:

spam?.eggs.cheese becomes this pseudo-AST (I've skipped the loads and maybe some other stuff):

                value=Name(id='spam'), attr='eggs', uptalk=True),
            attr='cheese', uptalk=False))

… which is then compiled as this pseudo-bytecode:

    LOAD_NAME 'spam'
    POP_JUMP_IF_NONE :label
    LOAD_ATTR 'eggs'
    LOAD_ATTR 'cheese'

I've invented a new opcode POP_JUMP_IF_NONE, but it should be clear what it does. I think it's clear how replacing spam with any other expression works, and how subscripting works. So the only question is whether understanding how .eggs.cheese becomes a pair of LOAD_ATTRs is sufficient to understand how ?.eggs.cheese becomes a JUMP_IF_NONE followed by the same pair of LOAD_ATTRs through the same two steps.

I suppose the reference documentation wording is also important here, to explain that an uptalked attributeref or subscription short-circuits the whole primary.

More information about the Python-ideas mailing list