[Python-ideas] Null coalescing operators

Chris Angelico rosuav at gmail.com
Sat Sep 19 00:37:17 CEST 2015

On Sat, Sep 19, 2015 at 3:42 AM, Mark Haase <mehaase at gmail.com> wrote:
> StackOverflow has many questions on the topic of null coalescing operators
> in Python, but I can't find any discussions of them on this list or in any
> of the PEPs. Has the addition of null coalescing operators into Python ever
> been discussed publicly?
> Python has an "or" operator that can be used to coalesce false-y values, but
> it does not have an operator to coalesce "None" exclusively.

Python generally doesn't special-case None, so having a bit of magic
that works only on that one object seems a little odd. For comparison
purposes, Pike has something very similar to what you're describing,
but Pike *does* treat the integer 0 as special, so it makes good sense
there. Pike code that wants to return "a thing or NULL" will return an
object or the integer 0, where Python code will usually return an
object or None. I can't think of any situation in Python where the
language itself gives special support to None, other than it being a
keyword. You're breaking new ground.

But in my opinion, the practicality is worth it. The use of None to
represent the SQL NULL value [1], the absence of useful return value,
or other "non-values", is pretty standard. I would define the operator
pretty much the way you did above, with one exception. You say:

created?.isoformat() # is equivalent to
created.isoformat() if created is not None else None

but this means there needs to be some magic, because it should be
equally possible to write:

created?.year # equivalent to
created.year if created is not None else None

which means that sometimes it has to return None, and sometimes
(lambda *a,**ka: None). Three possible solutions:

1) Make None callable. None.__call__(*a, **ka) always returns None.
2) Special-case the immediate call in the syntax, so the equivalencies
are a bit different.
3) Add another case: func?(args) evaluates func, and if it's None,
evaluates to None without calling anything.

Option 1 would potentially mask bugs in a lot of unrelated code. I
don't think it's a good idea, but maybe others disagree.

Option 2 adds a grammatical distinction that currently doesn't exist.
When you see a nullable attribute lookup, you have to check to see if
it's a method call, and if it is, do things differently. That means
there's a difference between these:

func = obj?.attr; func()

Option 3 requires a bit more protection, but is completely explicit.
It would also have use in other situations. Personally, I support that
option; it maintains all the identities, is explicit that calling None
will yield None, and doesn't need any magic special cases. It does add
another marker, though:

created?.isoformat?() # is equivalent to
created.isoformat() if created is not None and created.isoformat is
not None else None

As to the syntax... IMO this needs to be compact, so ?. has my
support. With subscripting, should it be "obj?[idx]" or "obj[?idx]" ?
FWIW Pike uses the latter, but if C# uses the former, there's no one
obvious choice.


[1] Or non-value, depending on context

More information about the Python-ideas mailing list