[Python-ideas] Null coalescing operators

Steven D'Aprano steve at pearwood.info
Mon Sep 21 16:41:31 CEST 2015


On Mon, Sep 21, 2015 at 11:55:55AM +0100, Paul Moore wrote:
> On 21 September 2015 at 03:35, Mark E. Haase <mehaase at gmail.com> wrote:
> > A) Is coalesce a useful feature? (And what are the use cases?)
> 
> There seem to be a few main use cases:
> 
> 1. Dealing with functions that return a useful value or None to signal
> "no value". I suspect the right answer here is actually to rewrite the
> function to not do that in the first place. "Useful value or None"
> seems like a reasonable example of an anti-pattern in Python.

I think that's a bit strong. Or perhaps much too strong.

There are times where you can avoid the "None or value" pattern, since 
there is a perfectly decent empty value you can use instead of None. 
E.g. if somebody doesn't have a name, you can use "" instead of None, 
and avoid special treatment.

But that doesn't always work. Suppose you want an optional (say) Dog 
object. There isn't such a thing as an empty Dog, so you have to use 
some other value to represent the lack of Dog. One could, I suppose, 
subclass Dog and build a (singleton? borg?) NoDog object, but that's 
overkill and besides it doesn't scale well if you have multiple types 
that need the same treatment.

So I don't think it is correct, or helpful, to say that we should avoid 
the "None versus value" pattern. Sometimes we can naturally avoid it, 
but it also has perfectly reasonable uses.


> Overall, I don't think coalesce is *that* useful, given that it seems
> like it'd mainly be used in situations where I'd recommend a more
> strategic fix to the code.

Go back to the original use-case given, which, paraphrasing, looks 
something like this:

result = None if value is None else value['id'].method()

I don't think we can reject code like the above out of hand as 
un-Pythonic or an anti-pattern. It's also very common, and a little 
verbose. It's not bad when the value is a name, but sometimes it's an 
expression, in which case it's both verbose and inefficient:

result = None if spam.eggs(cheese) is None else spam.eggs(cheese)['id'].method()

Contrast:

result = spam.eggs(cheese)?['id'].method()

which only calculates the expression to the left of the ?[ once.

An actual real-life example where we work around this by using a 
temporary name that otherwise isn't actually used for anything:

mo = re.match(needle, haystack)
if mo:
    substr = mo.group()
else:
    substr = None


I think it is perfectly reasonable to ask for syntactic sugar to avoid 
having to write code like the above:

substr = re.match(needle, haystack)?.group()


That's not to say we necessarily should add sugar for this, since 
there is no doubt there are disadvantages as well (mostly that many 
people dislike the ? syntax), but in principle at least it would 
certainly be nice to have and useful.


> > B) If it is useful, is it important that it short circuits? (Put another
> > way, could a function suffice?)
> 
> Short circuiting is important, but to me that simply implies that the
> "useful value or None" approach is flawed *because* it needs
> short-circuiting to manage.

Nothing needs short-circuiting, at least in a language with imperative 
assignment statements. You can always avoid the need for short-circuits 
with temporary variables, and sometimes that's the right answer: not 
everything needs to be a one-liner, or an expression.

But sometimes it is better if it could be.


> > C) If it should be an operator, is "??" an ugly spelling?
> >
> >     >>> retries = default ?? cls.DEFAULT

I assume the ?? operator is meant as sugar for:

retries = cls.DEFAULT if default is None else default

I prefer to skip the "default" variable and use the standard idiom:

if retries is None:
     retries = cls.DEFAULT

I also worry about confusion caused by the asymmetry between ?? and the 
other three ? cases:

# if the left side is None, return None, else evaluate the right side
spam?.attr
spam?['id']
spam?(arg)

# if the left side is None, return the right side, else return the left
spam ?? eggs

but perhaps I'm worried over nothing.


-- 
Steve


More information about the Python-ideas mailing list