On Mon, Sep 21, 2015 at 11:55:55AM +0100, Paul Moore wrote:
On 21 September 2015 at 03:35, Mark E. Haase firstname.lastname@example.org wrote:
A) Is coalesce a useful feature? (And what are the use cases?)
There seem to be a few main use cases:
- 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()
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.