On Mon, Sep 28, 2015 at 12:43 PM, Carl Meyer <carl@oddbird.net> wrote:
On 09/28/2015 12:38 PM, Guido van Rossum wrote:
> On Mon, Sep 28, 2015 at 10:38 AM, Carl Meyer <carl@oddbird.net

>     "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
>     from:
>
>         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 see that. The distinction is "short-circuit" vs "propagate."
Short-circuit is definitely more comprehensible and palatable.

Right.
 
[snip]
> It should not escape parentheses.

Good. I assume that the short-circuiting would follow the precedence
order; that is, nothing with looser precedence than member and index
access would be short-circuited. So, for example,

    foo?.bar['baz'].spam

would short-circuit the indexing and the final member access, translating to

    foo.bar['baz'].spam if foo is not None else None

but

    foo?.bar or 'baz'

would mean

    (foo.bar if foo is not None else None) or 'baz'

and would never evaluate to None. Similarly for any operator that binds
less tightly than member/index access (which is basically all Python
operators).

Correct. The scope of ? would be all following .foo, .[stuff], or .(args) -- but stopping at any other operator (including parens).
 
AFAICS, under your proposed semantics what I said above is still true, that

    x = foo?.bar.baz

would necessarily have a different meaning than

    temp = foo?.bar
    x = temp.baz

Or put differently, that whereas these two are trivially equivalent (the
definition of left-to-right binding within a precedence class):

    foo.bar.baz
    (foo.bar).baz

these two are not equivalent:

   foo?.bar.baz
   (foo?.bar).baz

Right.
 
I'm having trouble coming up with a parallel example where the existing
short-circuit operators break "extractibility" of a sub-expression like
that.

Why is that an interesting property?
 
I guess this is because the proposed short-circuiting still "breaks out
of the precedence order" in a way that the existing short-circuiting
operators don't. Both member access and indexing are within the same
left-to-right binding precedence class, but the new operators would have
a short-circuit effect that swallows operations beyond where normal
left-to-right binding would suggest their effect should reach.

Are there existing examples of behavior like this in Python that I'm
missing?

I don't know, but I think you shouldn't worry about this.
 
--
--Guido van Rossum (python.org/~guido)