
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)