
On 1 October 2015 at 12:39, Andrew Barnert via Python-ideas <python-ideas@python.org> wrote:
On Sep 30, 2015, at 18:08, MRAB <python@mrabarnett.plus.com> wrote:
It's only just occurred to me that there's a small inconsistency here. The "?.", "?[" and "?(" will short-circuit on None, whereas the "??" will short-circuit on non-None.
Would that cause any confusion in practice?
I noticed this when I was trying to write out grammar, sample ASTs, and sample bytecode for these things. I went searching the thread and saw no one had pointed it out. I went through docs and blogs for other languages, and didn't see anyone pointing out, complaining about, or offering to clear up any confusion. So I figured I wouldn't mention it, and see if anyone else even noticed.
The fact that an experienced programmer like you didn't even notice it until after a zillion messages over a span of weeks, and apparently nobody else did at all, seems to imply that there's no dangerously misleading intuitive parallel here.
(By the way, I have a feeling that including ?= will increase the risk of confusion, but I have no idea where that feeling comes from, so it may just be random noise in my head, maybe because I like ?. but don't particularly like ?= or something...)
Because unlike "?.", "?[" and "?(", "?=" would also shortcircuit on "if not None" if expanded as a ternary expression: target = target if target is not None else default However, it's possible to make it consistent by instead expanding it as: if target is None: target = default As a result, one interesting way of looking at this problem is to ask: what if we *only* offered the conditional operations, *without* offering a binary null-coalescing operator? That is, we could allow conditional assignment: data ?= [] headers ?= {} arg ?= make_default() With the meaning: if data is None: data = [] if headers is None: headers = {} if arg is None: arg = make_default() That would deal directly with the idiomatic case of handling a default argument of "None" and replacing it with a mutable container or expensive to calculate default value. Multiple levels of coalescence would need to be spelled out as multiple statements rather than as chained binary operations (in this case, I think putting it all on one line helps make the "first non-None value wins" semantics clearer): title ?= user_title; title ?= local_default_title; title ?= global_default_title The semantics of dict.setdefault() could also potentially be made clearer, as "d[key] ?= make_default()" could become a short circuiting equivalent of "dict.setdefault(k, make_default())": try: d[key] except KeyError: d[key] = make_default() Custom sentinels would still need to be spelled out with an if statement: if arg is _sentinel: arg = make_default() If they were offered, conditional attribute access, conditional item lookup and conditional calls would use the same "LHS is None" check, but in a ternary expression rather than an if statement, with the following: title?.upper() person?['name'] func?(arg) being equivalent to: None if title is None else title.upper() None if person is None else person['name'] None if func is None else func(arg) Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia