[Python-ideas] Null coalescing operators

C Anthony Risinger anthony at xtfx.me
Mon Sep 21 06:17:00 CEST 2015

On Sat, Sep 19, 2015 at 7:06 AM, Steven D'Aprano <steve at pearwood.info>

> On Sat, Sep 19, 2015 at 03:17:07AM -0500, C Anthony Risinger wrote:
> > I really liked this whole thread, and I largely still do -- I?think --
> but
> > I'm not sure I like how `?` suddenly prevents whole blocks of code from
> > being evaluated. Anything within the (...) or [...] is now skipped (IIUC)
> > just because a `?` was added, which seems like it could have side effects
> > on the surrounding state, especially since I expect people will use it
> for
> > squashing/silencing or as a convenient trick after the fact, possibly in
> > code they did not originally write.
> I don't think this is any different from other short-circuiting
> operators, particularly `and` and the ternary `if` operator:
> result = obj and obj.method(expression)
> result = obj.method(expression) if obj else default
> In both cases, `expression` is not evaluated if obj is falsey. That's
> the whole point.

Sure, but those all have white space and I can read what's happening. The
`?` could appear anywhere without break. I don't like that, but, opinion.

> > If the original example included a `?` like so:
> >
> >     response = json.dumps?({
> >         'created': created?.isoformat(),
> >         'updated': updated?.isoformat(),
> >         ...
> >     })
> >
> > should "dumps" be None, the additional `?` (although though you can
> barely
> > see it) prevents *everything else* from executing.
> We're still discussing the syntax and semantics of this, so I could be
> wrong, but my understanding of this is that the *first* question mark
> prevents the expressions in the parens from being executed:
> json.dumps?( ... )
> evaluates as None if json.dumps is None, otherwise it evaluates the
> arguments and calls the dumps object. In other words, rather like this:
> _temp = json.dumps  # temporary value
> if _temp is None:
>     response = None
> else:
>     response = _temp({
>         'created': None if created is None else created.isoformat(),
>         'updated': None if updated is None else updated.isoformat(),
>         ...
>         })
> del _temp
> except the _temp name isn't actually used. The whole point is to avoid
> evaluating an expression (attribute looking, index/key lookup, function
> call) which will fail if the object is None, and if you're not going to
> call the function, why evaluate the arguments to the function?

Yes that is how I understand it as well. I'm suggesting it's hard to see. I
understand the concept as "None cancellation", because if the left is None,
the right is cancelled. This lead me here:

* This is great, want to use all the time!
* First-level language support, shouldn't I use? Does feels useful/natural
* How can I make my APIs cancellation-friendly?
* I can write None-centric APIs, that often collapse to None
* Now maybe user code does stuff like `patient.visits?.september?.records`
to get all records in September (if any, else None)
* Since both `?` points would *prefer* None, if the result is None, I now
have to jump around looking for who done it
* If I don't have debugger ATM, I'm breaking it up a lot for good 'ol
print(...), only way
* I don't think I like this any more :(

I especially don't like the idea of seeing it multiple times quickly, and
the potential impact to debugging. The truth is I want to like this but I
feel like it opens a can of worms (as seen by all the wild operators this
proposal "naturally" suggests).

> > Usually when I want to use this pattern, I find I just need to write
> things
> > out more. The concept itself vaguely reminds me of PHP's use of `@` for
> > squashing errors.
> I had to look up PHP's @ and I must say I'm rather horrified. According
> to the docs, all it does is suppress the error reporting, it does
> nothing to prevent or recover from errors. There's not really an
> equivalent in Python, but I suppose this is the closest:
> # similar to PHP's $result = @(expression);
> try:
>     result = expression
> except:
>     result = None
> This is nothing like this proposal. It doesn't suppress arbitrary
> errors. It's more like a conditional:
> # result = obj?(expression)
> if obj is None:
>     result = None
> else:
>     result = obj(expression)
> If `expression` raises an exception, it will still be raised, but only
> if it is actually evaluated, just like anything else protected by an
> if...else or short-circuit operator.

I did say vaguely :) but it is extremely hideous I agree. The part that
made me think of this is the would be desire for things to become None (so,
or example, wanting to avoid throwing typed/informative exceptions if
possible) so they'd then be more useful with `?`.


C Anthony
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20150920/d8250f22/attachment.html>

More information about the Python-ideas mailing list