Mark E. Haase writes:
This conversation has really focused on the null aware attribute access, but the easier and more defensible use case is the null coalesce operator, spelled "??" in C# and Dart. It's easy to find popular packages that use something like "retries = default if default is not None else cls.DEFAULT"
To me, it's less defensible. Eg, currently TOOWTDI for "??" is the idiom quoted above. I sorta like the attribute access, attribute fetch, and function call versions, though I probably won't use them.
Also some functions need to accept None as an actual argument, and the module defines a module-specific sentinel. The inability to handle such sentinels is a lack of generality that the "x if x is not sentinel else y" idiom doesn't suffer from, so "??" itself can't become TOOWTDI.
I don't write "def foo(default):" (ever that I can recall), so using "default" in
retries = default if default is not None else cls.DEFAULT
confuses me. Realistically, I would be writing
retries = retries if retries is not None else cls.RETRIES
(or perhaps the RHS would be "self.retries"). That doesn't look that bad to me (perhaps from frequent repetition). It's verbose, but I don't see a need to chain it, unlike "?.". For "?.", some Pythonistas would say "just don't", but I agree that often it's natural to chain.
to supply default instances. Other packages do something like "retries = default or cls.DEFAULT", which is worse because it easy to overlook the implicit coalescing of the left operand.
Worse? It's true that it's more risky because it's all falsies, not just the None sentinel, but I think "consenting adults" applies here.
I don't know about the packages you looked at, but I often use "x = s or y" where I really want to trap the falsey value of the expected type, perhaps as well as None, and I use the "x if s is not sentinel else y" idiom to substitute default values. I also use "or" in scripty applications and unit test setup functions where I want compact expression and I don't expect long-lived objects to be passed so I can easily figure out where the non-None falsey came from anyway.
A) Is coalesce a useful feature? (And what are the use cases?)
Yes, for the whole group of operators. Several use cases for the other operators have already been proposed, but I wouldn't use them myself in current or past projects, and don't really foresee that changing. -0 for the group on the IAGNI principle.
But for "??" specifically, it's just more compact AFAICS. I don't see where I would use x ?? y ?? z, so the compactness doesn't seem like that great a benefit. In practice, I think the use cases for "??" would be a strict subset of the use cases for the ternary operator, so you have to argue that "this special case *is* special enough" to have its own way to do it. I don't think it is. -1
C) If it should be an operator, is "??" an ugly spelling?
>>> retries = default ?? cls.DEFAULT
Looks like metasyntax from pseudo-code that didn't get fixed to me. That would probably change if other ?x operators were added though.
I have no comment on short-circuiting (no relevant experience), or keyword vs. punctuation spellings. On second thought:
D) If it should be an operator, are any keywords more aesthetically pleasing? (I expect zero support for adding a new keyword.)
>>> retries = default else cls.DEFAULT
I kinda like this if-less else syntax for the symmetry with else-less if. But on second thought I think it would persistently confuse me when reading, because it would be extremely natural to expect it to be another way of spelling "default or cls.DEFAULT". "try ... else ..." also has its attraction, but I suppose that would fail for the same reasons that the ternary operator is spelled "x if y else z" rather than "if y then x else z".