Not coalescing around None-aware
The wish for a "None-aware" operator (or operators) is longstanding within the Python community. While there is fairly widespread interest in more easily handling situations where a value needs to be tested for being None before being further processed, there is much less agreement on how to "spell" such an operator (or construct) and on whether the language truly needs it. But the idea never seems to go away, with long discussions erupting every year or two—and no resolution really in sight.
PEP 505
We looked at the idea back in 2018, but PEP 505 ("None-aware operators") goes back to 2015; it seems likely that the idea had been discussed well before that as well. At the time of our article, the Python community was still working on figuring out how it would be governed after Guido van Rossum stepped down from his benevolent dictator role. PEPs were not being accepted or rejected because of that and PEP 505 was deferred.
In Python, it is not uncommon to see code like the following (taken from the more extensive examples in our earlier article):
if x is None: x = 42The PEP proposes the "??" None-aware (also known as null-coalescing) operator. It returns the left-hand side if it is not None, otherwise the right-hand side is evaluated and returned. So that code above could be replaced with:
x = x ?? 42There is also an "augmented assignment" version ("??=") and two operators that apply the None-awareness to indexing ("?[]") and to attribute access ("?.") proposed as well:
x ??= 42 # same as above x?['foo'] # retrieve dict entry 'foo' if x is not None x?.foo # retrieve attribute foo if x is not NoneIn the last two examples above, if x is None, then the expression evaluates to None as well.
The PEP has come up again a few times since the Python governance upheaval
put it on the back burner, including in a Python
discussion-forum thread back in April 2021. Kumar Aditya asked
about adding a null-coalescing operator; Steven D'Aprano pointed
to the PEP and some of the previous
discussions. Along the way, PEP co-author Steve Dower said
that his plan for the PEP is to drop it "because I now think the additional syntax would not make Python cleaner or a better language
".
The PEP came up again in October 2021 when Doug Swarin raised
it on
the python-dev mailing list. As with Aditya and others, Swarin pointed
to the existence of similar operators in other languages (such as
JavaScript) and said that it would "result in a significant reduction in
boilerplate code
" testing for None. He suggested that the
indexing ("maybe subscript") and attribute-access ("maybe dot") operators
might actually lead to a decrease in code readability, so perhaps they
could be removed from the PEP, though he was not personally in favor of
doing so. He had also created a pure-Python implementation that he made
available for testing.
Van Rossum, who has generally had a favorable inclination toward the PEP
all along, thanked
Swarin and said that he hoped more discussion would "help convince the
Steering Council to accept it
". The conversation proceeded along
familiar lines, with some strongly advocating for at least the addition of
?? and ??=, while others were expressing concerns about the
readability. The specter of "Perl-ification" was present as well; to
some, adding more operators of this sort just makes the language look like
Perl (or, worse still, line noise).
Revival
On December 11, "yingshaoxo" revived the forum thread that started 18 months earlier and the discussion was off and running again. Yingshaoxo advocated adopting the feature, while noting that multiple languages (JavaScript, Flutter, Kotlin, and C#) already have it. Cameron Simpson noted that there is an existing way to handle the example yingshaoxo posted, but that it does not work in all cases:
# proposed new syntax new_variable = a_variable ?? 'Hello Python' # "same" thing with existing syntax new_variable = a_variable or 'Hello Python'That relies on a_variable evaluating to a false value, which is the case for None, but it is also the case for the empty string, 0, and a few other values. D'Aprano pointed out that the same argument was made to resist adding a ternary operator to the language, until PEP 308 ("Conditional Expressions") was abruptly adopted because of the problems that can occur with expressions that unexpectedly evaluate to false:
For many, many years we rejected every request for a ternary if operator with the argument that you can just writecondition and if_true_value or if_false_valueE.g. len(mylist) > 0 and mylist[0] or "default". Can you see the bug? (Hint: what if the value of mylist[0] is, say, the empty string?).
What followed was a long discussion on "readability". "Fancidev" asserted that the conditional version was more readable:
"y if x is None else x" is more readable than "x ?? y".Since code is written once and read 100 times, it's probably worth the extra typing.
As might be guessed, others disagreed. Tzu-ping Chung said
that the conditional form "requires more mental attention
", but did
recognize that there is a potential hurdle in needing to learn what
?? means. D'Aprano said
that those coming to Python from other languages often complain that the
conditional construct in Python is unreadable. "So maybe readability
depends on what you are used to.
"
Fancidev pointed out that ?? can only be applied to a subset of the situations where the more general conditional expression can be used; it requires learning about the operator but can then only be applied to None tests. "Vladimir" agreed, but linked to a section of PEP 505 that argues for ?? because it is more concise. The PEP has an example of initializing default values:
data = [] if data is None else data # or data = data if data is not None else []Either of those will work, but the first puts the operands in an "
unintuitive order", while the second is a bit longer and repetitive (i.e. "data if data"). Using the proposed operator, it would simply be:
data = data ?? []To Fancidev, those examples just reinforce their belief that the conditional version is more readable, but Chris Angelico argued that "readability" is totally subjective, at least as it is used in discussions like these:
I personally suspect that some people consider something "readable" on the basis that "I can understand what it does based on my pre-existing knowledge of what Python can already do" (meaning that new syntax is ALWAYS less 'readable' than a verbose form that already exists), and other people consider something "readable" on the basis that it is compact and expresses a thought that can be fit into a larger "sentence" or "paragraph" (meaning that a new syntax is almost always more 'readable' than the more-verbose form that already exists).
Coalesce
Marc-André Lemburg took
a different tack on the idea. He pointed to the PostgreSQL
COALESCE() function as a possible model for "an explicit and
elegant way
" to solve many of the problems that PEP 505
describes. He suggested adding a Python builtin along the
following lines:
coalesce(*args, /, check=None, logic=False)Return the first value from args which is not the check object (defaults to None). If check is a callable, return the first value from args which check(value) is False. logic may be set to True, to swap the test logic.
The "/" in the argument list separates the positional-only parameters (args in this case) from those that can be specified by position or keyword (check and logic). He goes on to list a few operators as possibilities for the check values, such as a new list.isempty() or the existing math.isnan().
But there is a problem with that approach, as several people pointed out:
there is no ability to short-circuit the evaluation of some of the arguments.
Using a conditional expression (or the proposed ?? operator) would
not evaluate the "else" argument if the value is not None. As
Fancidev put
it: "it does not lazily evaluate the arguments, which I imagine will
be a necessary requirement
". Peter Suter added
some other ways where the operator approach is superior, including:
Chaining is much clearer and less error prone with operators:(override ?? fallback).name ?? default coalesce(coalesce(override, fallback).name, default)
Meanwhile, the "debate" over readability continued, much of it between Angelico
and Vladimir, seemingly without changing any minds. In fact, early on in
that part of the discussion, Angelico hit the nail on the head. In a
message linked above, he decried the idea of "readability" because:
"Everyone has their own definition, never backed by any sort of actual
studies, and nobody ever changes their mind based on other people's
examples.
" Eventually, after a good bit of back and forth on
readability, the thread was locked on December 17.
As should be clear, though, it is a topic that comes up often; until the PEP is either accepted or rejected, it will undoubtedly come up again. Even if the PEP is rejected, it will not be a huge surprise to see the idea pop up again; it has obvious utility and other languages have something similar, which will make it that much more desirable. The arguments against the simplest form (just ?? and ??=) seem to mostly boil down to being a barrier to learning or understanding the language. But, as D'Aprano put it, Python has features for both casual and more serious programmers:
The beauty of Python is that it is accessible to casual programmers. You don't have to use null-coalescing operators any more than you have to write classes, or use closures, or use threads.But we didn't let those casual programmers stand in the way of Python getting classes, closures, threads, async, regexes etc. Let the casual programmers continue to write using the basic features, and the power users use the power features.
It is hard to predict what will happen from here, especially given that the PEP's authors are no longer pushing it; it seems likely that some other core developer(s) would need to sponsor it for the newly elected steering council to even consider it. If that happens, perhaps an alternate spelling can be found to reduce the unhappiness with the "ugliness" of ??. A reduction in scope (eliminating ?. and ?[]) might also help the prospects of the PEP; down the road those additions could be considered again. One way or another, getting PEP 505 out of limbo would help clarify things quite a bit.
Index entries for this article | |
---|---|
Python | None |
Python | Python Enhancement Proposals (PEP)/PEP 505 |
Posted Dec 22, 2022 20:25 UTC (Thu)
by developer122 (guest, #152928)
[Link] (7 responses)
It wouldn't even have to literally be the word none necessarily, just something slightly more descriptive to hint in the right direction.
Posted Dec 22, 2022 21:46 UTC (Thu)
by smurf (subscriber, #17840)
[Link] (6 responses)
Posted Dec 22, 2022 22:32 UTC (Thu)
by mathstuf (subscriber, #69389)
[Link]
Posted Dec 23, 2022 8:11 UTC (Fri)
by Wol (subscriber, #4433)
[Link] (3 responses)
They long pre-date Perl.
I've ALWAYS used them (where available) in preference to "=" or "==" or "!=" or "<>".
The trouble with "=" and "==" is they are ambiguous - they stink of *assignment*. How many bugs do you get in C because people accidentally use "assign" when they meant "compare"?
I've used EQ and NE for over 40 years, and they were in use maybe 20 (or more) years before that!
Cheers,
Posted Dec 23, 2022 9:33 UTC (Fri)
by smurf (subscriber, #17840)
[Link] (2 responses)
One doesn't exclude the other.
> The trouble with "=" and "==" is they are ambiguous
are they? "=" is assignment and "==" is comparison. This holds for so many languages that by now the confusion is on the side of those who _don't_ do things this way, IMHO.
> How many bugs do you get in C because people accidentally use "assign" when they meant "compare"?
None, nowadays, because compilers warn about assignments in expressions.
Posted Dec 23, 2022 10:00 UTC (Fri)
by Wol (subscriber, #4433)
[Link] (1 responses)
> One doesn't exclude the other.
I notice you cut the line before that, which means your comment now doesn't make sense. EQ and NE were *borrowed* from elsewhere. For the OP maybe they stink of Perl. For myself (and presumably many others) I'm not that familiar with Perl but I'm very familiar with EQ and NE. How does that "stink of Perl"?
> > The trouble with "=" and "==" is they are ambiguous
> are they? "=" is assignment and "==" is comparison. This holds for so many languages that by now the confusion is on the side of those who _don't_ do things this way, IMHO.
Okay, so now EVERY language has to do things the C way? What are you going to do about languages where "==" means something other than equality? (just because I can't think of any doesn't mean there aren't any!) Or languages where "=" is context sensitive and could mean either (I use languages like that all the time - one reason I prefer EQ and NE!!!) (I can't remember, I don't believe FORTRAN used "=" for equality ...)
> > How many bugs do you get in C because people accidentally use "assign" when they meant "compare"?
> None, nowadays, because compilers warn about assignments in expressions.
So you get the opposite - where a naive programmer "corrects" the warning, only to discover that the original programmer *intentionally* put an assignment in an if statement ... (I guess "if (result = function()) {" is not uncommon?)
Murphy's law - if your language syntax contains land mines, then at some point you WILL get blown up. And all the defensive programming in the world won't save you, when some newbie comes in, misunderstands it, and treads on the land mine.
Cheers,
Posted Dec 24, 2022 19:51 UTC (Sat)
by NYKevin (subscriber, #129325)
[Link]
Javascript uses == to mean "loose" equality, which tries to coerce the types of the LHS and the RHS to match before comparing them. Consequently, it is intransitive and can be very unpredictable in certain contexts. They spell true or "strict" equality as ===. The == operator is not (typically) used in modern Javascript, so this is mostly just a quirk of spelling.
= is still assignment, however.
Posted Jan 5, 2023 15:45 UTC (Thu)
by azz (subscriber, #371)
[Link]
Posted Dec 22, 2022 21:44 UTC (Thu)
by jbills (subscriber, #161176)
[Link] (3 responses)
Posted Dec 23, 2022 8:46 UTC (Fri)
by amarao (guest, #87073)
[Link] (2 responses)
Posted Dec 26, 2022 23:11 UTC (Mon)
by mathstuf (subscriber, #69389)
[Link] (1 responses)
Only if it wants to fizz out :) .
Posted Dec 30, 2022 17:30 UTC (Fri)
by ssmith32 (subscriber, #72404)
[Link]
Posted Dec 22, 2022 21:45 UTC (Thu)
by smurf (subscriber, #17840)
[Link] (15 responses)
Or, even shorter, it could (probably should, in this case) be
data ??= []
Frankly while I'm no fan whatsoever of Perl-ifying Python syntax, but I'm even less a fan of the various "if None" dances that it'd replace, so ?? and ??= would be a net win IMHO.
I'm not so sure about the others, though.
Posted Dec 23, 2022 11:07 UTC (Fri)
by adobriyan (subscriber, #30858)
[Link] (14 responses)
a ≟ []
Posted Dec 23, 2022 12:01 UTC (Fri)
by smurf (subscriber, #17840)
[Link] (8 responses)
How to add all of these to your keyboard is left as an exercise to the reader.
Posted Dec 23, 2022 12:04 UTC (Fri)
by adobriyan (subscriber, #30858)
[Link] (4 responses)
Posted Dec 23, 2022 12:09 UTC (Fri)
by Wol (subscriber, #4433)
[Link] (2 responses)
(As a dinosaur, my favourite IDE is a sheet of a4 :-)
Cheers,
Posted Dec 23, 2022 13:11 UTC (Fri)
by jem (subscriber, #24231)
[Link] (1 responses)
Posted Dec 23, 2022 18:06 UTC (Fri)
by Wol (subscriber, #4433)
[Link]
My first boss actually spent his first six months at the company without a computer, so he had no choice BUT to use A4.
(When typed in to the shiny new (actually, second hand) computer, when it arrived, his programs apparently compiled and ran without error ...)
Cheers,
Posted Dec 30, 2022 20:54 UTC (Fri)
by salimma (subscriber, #34460)
[Link]
Posted Dec 23, 2022 21:02 UTC (Fri)
by NYKevin (subscriber, #129325)
[Link]
R already uses <- for assignment. For symmetry, it also has -> which lets you write a backwards assignment. Somehow, I don't see any other languages adopting this as a standard (even ignoring the fact that C and C++ already have a different -> operator).🙃
Posted Dec 25, 2022 22:34 UTC (Sun)
by mtaht (subscriber, #11087)
[Link] (1 responses)
I'm not kidding btw. I'd really like it if more code looked like the math, and an IDE would make it as easy as α, β, Γ. The Δ between well known symbols and their overly verbose english equivalents would increase visual density.
Posted Jan 15, 2023 18:08 UTC (Sun)
by sammythesnake (guest, #17693)
[Link]
> for x ∈ Y
(which you can also spell "for x in Y" if you prefer)
and groks "≠" & "≥" as alternate spellings of "!=" & ">=" and so on. ("🫖≠☕" might be perfectly valid code :-P)
The REPL, Jupyter notebooks, and IDEs with support all expand LaTeX escapes for you (e.g. "\in[tab]" becomes "∈" and similarly for all the fun Greek letters / logic symbols / alchemical symbols / emojis etc.) so you don't need a fancy million-key keyboard :-D (Or you can map right-Alt to the compose key if you like)
Personally, I don't have a mathematics background and find many of these symbols somewhat opaque - it doesn't help that they're difficult to Google (but then so are all the punctuation marks that get so much use in programming language syntax!)
It's also an interesting language in its own right that I'm toying around with...
Posted Dec 23, 2022 20:59 UTC (Fri)
by LtWorf (subscriber, #124958)
[Link] (4 responses)
Posted Dec 26, 2022 3:09 UTC (Mon)
by anselm (subscriber, #2796)
[Link] (3 responses)
In Python 3, there's nothing preventing you from saying
Posted Dec 26, 2022 10:36 UTC (Mon)
by smurf (subscriber, #17840)
[Link]
Posted Dec 27, 2022 0:20 UTC (Tue)
by LtWorf (subscriber, #124958)
[Link] (1 responses)
if x ∈ X ∨ y ≤ z: ...
Posted Dec 27, 2022 10:08 UTC (Tue)
by smurf (subscriber, #17840)
[Link]
Posted Dec 22, 2022 21:54 UTC (Thu)
by Nahor (subscriber, #51583)
[Link] (6 responses)
"data = [] if data is None else data" is expressive, but it's not compact.
The two are mostly mutually exclusive so "readability" is about finding the right balance, and is very subjective.
Personally for what it's worth, and as a non-python user, I find the later more readable overall (dependent on how many other similar shortcuts exist which could cause confusion, e.g. I remember Perl being very compact to the point of obfuscation rather than readability :p ).
Posted Dec 23, 2022 6:22 UTC (Fri)
by zorro (subscriber, #45643)
[Link] (5 responses)
Posted Dec 23, 2022 10:42 UTC (Fri)
by mb (subscriber, #50428)
[Link] (4 responses)
I don't agree with that.
There are many patterns in programs and languages that a reader doesn't literally read and parse any more after getting used to it. And that pattern matching doesn't really depend a lot on the verboseness of the code.
Take for example a basic iteration loop in C:
for (int i = 0; i < length; i++)
Nobody (with experience) reads and parses that step by step.
The same thing happens with
data if data is not None else default
The big advantage of the verbose form is that you can *also* read and understand it, if you don't know the pattern. You can't do that with special syntax like ??.
Posted Dec 24, 2022 13:30 UTC (Sat)
by kid_meier (subscriber, #93987)
[Link] (1 responses)
I also don't think it's fair to say that special syntax always increases mental load. Your pattern matching machinery will work on the special syntax just as well as it works on the fully spelled out syntax--actually quite possibly better since there is less risk of false positives. As someone who writes Java for a living, I think it'd be a good trade. Java already has ternary operator but I'd still welcome a more specialized operator like this.
Posted Jan 5, 2023 15:33 UTC (Thu)
by kpfleming (subscriber, #23250)
[Link]
The example in the post you referred to may be the 'normal form', but remove the 'not' from it and the meaning changes significantly but a 'quick read' to look for patterns might overlook the change. I can't imagine an actual use for that expression, but I'd hate to misunderstand it because my brain assumed it was just the same as all the others it had seen.
Posted Dec 25, 2022 6:21 UTC (Sun)
by Nahor (subscriber, #51583)
[Link] (1 responses)
You're mixing cause and effect.
Note though that it will never be as fast as the "??" operator. In big part because you need to check that the words are what you expect them to be (Is the "data" after the "if" really "data" or is it "date" or "dota"? Is the "None" really "None" or is it "Note" or "Noon"? ...). Because of the more limited scope of "??", some of those questions just become irrelevant / have a single and necessary answer.
And that familiarity goes hand in hand with Zorro's comment. The "??" operator, while initially not expressive, will become more expressive as you get familiar with that construct.
> The big advantage of the verbose form is that you can *also* read and understand it, if you don't know the pattern
And we are back to "compact" vs "expressive". One is expressive but verbose, the other is compact but less expressive. One is more readable for newbies, the other for veterans. And people don't agree where the balance point should be.
> Saying that people don't have to [learn] ?? is also wrong, because other people will use it in their code and examples that Newbies will read
Nobody said anything like that. Everybody has to learn. Even the long expression needs to be learned for that matter, because it's not common to have the true-statement on the left of the "if", or that statement that returns a value ("else default") (*). The amount of learning effort is not the same, but it's still there in both case.
(*) more specifically, in most languages, "if" is a statement, which cannot have a value, while here in python, the "if" is an expression, which do have one.
Posted Dec 28, 2022 15:14 UTC (Wed)
by marcH (subscriber, #57642)
[Link]
Except for people who have to make a small change to a Python script rarely, only a couple times a months. I have and always had teammates in that case and that's where Python really shines compares to shell scripts or Perl or others. While the verbose " if None" variations annoy me every time, I totally understand the "Perlification" fear of "??".
Isn't there some middle ground alternative?
Posted Dec 22, 2022 22:00 UTC (Thu)
by andrewsh (subscriber, #71043)
[Link] (2 responses)
Posted Dec 22, 2022 23:56 UTC (Thu)
by NYKevin (subscriber, #129325)
[Link]
There are at least two ways to fix that:
* Use static type checking and the Optional type annotation. This requires third-party tools, as CPython does not check type annotations.
The fundamental problem you are going to run into is that Python is a dynamic language, which has had some static niceties bolted on after the fact. There are still people who want to use it as a dynamic language, and telling them "well, just don't use the parts of the language which are hard to read under dynamic typing" is not very nice.
Posted Jan 5, 2023 23:08 UTC (Thu)
by flussence (guest, #85566)
[Link]
Anyway it's a bit absurd to reject these proposals on the basis of "too Perlish". Being like Perl would entail doing almost nothing for 20 years and then devolving into bikeshedding chaos in a rushed attempt to catch up with languages that left them behind.
Posted Dec 23, 2022 0:14 UTC (Fri)
by shironeko (subscriber, #159952)
[Link] (5 responses)
Posted Dec 23, 2022 9:17 UTC (Fri)
by ceplm (subscriber, #41334)
[Link] (2 responses)
My problem with these operators (and a lot of what already happened to Python in the last few releases) is that it is similar to the Java discussion on the checked exceptions: it is completely dominated by people who didn’t understand the problem and they are trying to get rid of something which they consider just an annoyance. For me all those
Posted Dec 23, 2022 11:05 UTC (Fri)
by adobriyan (subscriber, #30858)
[Link] (1 responses)
class X:
x1 = X()
results in
[] []
so passing None as default value in constructor is obvious solution. I'm not sure how often this is seen in practice but not code smell for sure.
Posted Dec 23, 2022 11:49 UTC (Fri)
by smurf (subscriber, #17840)
[Link]
Well, "language smell" then – as there really should be a way to instantiate the default option(s) at the time they are needed, not when the "def" statement is compiled.
Attempts to add a way to do this to Python have not succeeded so far.
Posted Dec 23, 2022 20:15 UTC (Fri)
by iabervon (subscriber, #722)
[Link] (1 responses)
I think this particular example comes from a language design anti-pattern, where, in order to introduce a feature, you present the simplest case in which you could use it. It seems like an easy way to show what the feature can do, but changing the language is not worthwhile just to make a simple statement slightly simpler. There's a value in not repeating an expression, but it's hard to appreciate if the example expression is a single-character variable name. Consider: "if we're told to check size limits, and the size of the message's header, if any, plus the size of the message's body, if any, is greater than the limit, raise an exception". (And the message representation might be using complicated objects for header and body that may be expensive to get the length of if it isn't needed, and want to clearly distinguish the case of not having a body or header in other places by using None to represent this situation.) That's: With the feature, and: Or: With the feature, you can write something much closer to the specification, and if the next version of the code has to add "likewise for the message's footer", the programmer who copies the "header" section and changes "header" to "footer" can't miss the second place that "header" appeared. Furthermore, the first one consists entirely of text you'd want to read in order to understand what it's doing, while the others have portions you'd skim, and hope that they correctly implement "if necessary". Similarly, "(some long but descriptive expression)?.attribute" is much easier to understand than "(some long but descriptive expression).attribute if (probably the same thing but maybe not) is not None else None", while "x.y if x is not None else None" isn't worth the trouble. That said, I find the operator "??" doesn't feel Pythonic to me, and I think some short word would fit the Python better, but I can't think of a word that would suit; "?." also seems weird, but I can't think how to avoid using a weird symbol when the basic expression uses as operator already.
Posted Dec 23, 2022 20:48 UTC (Fri)
by shironeko (subscriber, #159952)
[Link]
Posted Dec 23, 2022 9:41 UTC (Fri)
by grawity (subscriber, #80596)
[Link]
Chaining is much clearer and less error prone with operators: This reminds me so much of the examples in the "matrix multiplication operator" PEP, just with
Posted Dec 23, 2022 10:34 UTC (Fri)
by rrolls (subscriber, #151126)
[Link] (14 responses)
I don't buy the arguments that "x ?? y" or "x ??= y" or "x?.y" or "x?[y]" "is unreadable", and I'd make plenty of use of all four of them - but it sounds like that's already been covered by the whole, "Everyone has their own definition... and nobody ever changes their mind" thing. (On the subject of readability, I avoid ever using the ternary "y if x else z" construction in my code, because I find _that_ unreadable: I've always maintained it ought to be written "if x then y else z", so I'll always write it out with a four-line if-else block instead.)
The one thing that doesn't make sense to me is the choice of precedence for the ?? operator such that:
> (a ?? 2 ** b ?? 3) == a ?? (2 ** (b ?? 3))
The explanation given for ?? to bind "more tightly than other binary operators" makes sense: one would hardly ever expect "x + y" (and the like) to return None, so "x + y ?? z" makes no sense as "(x + y) ?? z" but makes perfect sense as "x + (y ?? z)". So I'm surprised this logic wasn't applied consistently, making "a ?? 2 ** b ?? 3" equivalent to "(a ?? 2) ** (b ?? 3)", which is a valid non-None value whether a and/or b are None or not.
In particular, the PEP claims "parenthesizing the sub-expressions any other way would result in TypeError"... which, unless I'm missing something fundamental, is clearly false as I've just demonstrated by giving such an alternative parenthesization.
Posted Dec 23, 2022 10:51 UTC (Fri)
by mb (subscriber, #50428)
[Link] (8 responses)
Holy mother of god.
Posted Dec 23, 2022 11:56 UTC (Fri)
by rrolls (subscriber, #151126)
[Link] (7 responses)
It's not about putting ?? in absolutely everywhere, either - just making it available for the places where it's useful, and then letting developers choose appropriate places to use it. ?? is no different to walrus, the if-else ternary, lambda functions, generator expressions, or whatever other inline "shorthand" constructions you can think of in that regard. You can easily write ugly code with those if you so choose, just as you can with ??.
For example, a toy "print" function might include "stream.write(msg + end ?? '\n')" - totally readable and the obvious expression to write. Of course, in this case we can just put end='\n' instead of end=None in the function signature, but in more complex real-world cases, where the default might need to be an object or vary depending on circumstances or whatever, that's not always appropriate.
Resolving defaults before doing calculations is something I do all the time in current Python, and it has its downsides - it's extra clutter in the function, and it can be hard to track which variables need to be resolved, which don't, and whether you've missed any, especially if there's a lot of variables involved. Clever type checking code assist helps massively with that, but it's a fairly recent development and it isn't going to work in absolutely all scenarios - for example when extracting data nested deep in the result of a json.load call. Sometimes, it's just cleaner to resolve the defaults inline.
Posted Dec 23, 2022 14:46 UTC (Fri)
by shironeko (subscriber, #159952)
[Link] (6 responses)
Posted Dec 23, 2022 17:44 UTC (Fri)
by rrolls (subscriber, #151126)
[Link] (5 responses)
There are plenty of examples in the PEP that demonstrate where ?? would be useful far better than I could.
Posted Dec 23, 2022 19:30 UTC (Fri)
by shironeko (subscriber, #159952)
[Link] (4 responses)
One common example in there is the practice of foo(optional=None) and then assigning a default value to optional right afterwards. clearly this calls for improving the default value syntax not the none operator because it'll still be ugly, just shorter. Maybe the default syntax should accept function call, then we get the added benefit of having the function signature more self documenting than just None and have to figure out what that means.
Posted Dec 24, 2022 6:35 UTC (Sat)
by NYKevin (subscriber, #129325)
[Link] (3 responses)
Improving the default syntax has been discussed endlessly, but it's probably never going to happen, because both of the obvious approaches are non-starters:
1. You can't introduce a new default syntax, because then you would have two default syntaxes, and that has generally been regarded as inferior to the status quo.
Posted Dec 24, 2022 6:42 UTC (Sat)
by shironeko (subscriber, #159952)
[Link] (2 responses)
Posted Dec 24, 2022 9:12 UTC (Sat)
by NYKevin (subscriber, #129325)
[Link]
My interpretation of your comment is that you want to allow us to write an arbitrary expression as the default value, and lazily evaluate that expression when the function is called. It is already legal Python syntax to put arbitrary expressions as the default value, so this example will demonstrate the backwards incompatibility of the second half (lazy evaluation) only:
If you make x=i evaluate lazily, then both versions would print 4. This is backwards incompatible, and it also leaves us with no obvious way to get the 2 behavior instead of the 4 behavior. (Of course, it can be done, but it requires additional boilerplate that nobody should have to write.)
On the other hand, if you are proposing some new syntax that doesn't look like a "regular" Python expression (or somehow "decorates" the default value to indicate that it should be evaluated lazily)... nobody wants to deal with two different default value syntaxes in the same language. That way lies madness.
Posted Dec 24, 2022 9:14 UTC (Sat)
by smurf (subscriber, #17840)
[Link]
Personally I'd just re-purpose the Walrus operator to do lazy evaluation when you (ab)use it in a "def" declaration, though that's probably a non-starter given the past contentious discussion on that topic.
Posted Dec 24, 2022 11:26 UTC (Sat)
by k3ninho (subscriber, #50375)
[Link] (4 responses)
Same, there's stuff I've maintained in js/ts for pulling items out of a nested JSON blob that had to test for each level on the way down, which needed to go through the if parent / if parent.child1 / if parent.child1.child2 stack. This is a pattern that I need in the way I write Python. I'd love this PEP to help me avoid 'index out of range' errors for dictionary errors and attributes.
K3n.
Posted Dec 28, 2022 9:52 UTC (Wed)
by kleptog (subscriber, #1183)
[Link] (3 responses)
a.get('foo', {}).get('bar', {}).get('baz')
gets tedious after a while. And there's no easy equivalent for array indexes so you always have to handle them separately. Something like:
a?['foo']?['bar']?['baz']
is a win in every sense.
Posted Jan 5, 2023 9:43 UTC (Thu)
by Karellen (subscriber, #67644)
[Link] (2 responses)
Wouldn't it be easier to use an API that takes an XPath-style selector (or similar), where you can ask for a nested element and just get a single result (or None) at the end? e.g:
Posted Jan 5, 2023 11:10 UTC (Thu)
by kleptog (subscriber, #1183)
[Link] (1 responses)
Posted Jan 5, 2023 14:39 UTC (Thu)
by smurf (subscriber, #17840)
[Link]
Posted Dec 23, 2022 14:24 UTC (Fri)
by geoffhill (subscriber, #92577)
[Link] (2 responses)
This isn’t just about all the syntax-aware programs, libraries and models that will need updating.
Every language addition, however minor, has the potential to change the idiomatic style and practices of the language.
When that happens, it creates a huge amount of tech debt to refactor all non-idiomatic code in existence, and it reduces the average existing Python programmer’s language familiarity.
Syntax changes risks changing the character of the language that made it so popular in the first place.
Just stop!!!
Posted Dec 23, 2022 17:17 UTC (Fri)
by pwfxq (subscriber, #84695)
[Link]
> ...noting that multiple languages (JavaScript, Flutter, Kotlin, and C#) already have it
Just because other languages have a feature doesn't mean the Python language should adopt it.
Personally I'm not in favour of adding this ever so slightly cryptic syntax to Python.
Posted Dec 25, 2022 16:36 UTC (Sun)
by caliloo (subscriber, #50055)
[Link]
one of the main feature of python imho is ease of learning. Adding more operators for those who have 5 -10 years experience in it and just want to code faster those places where checking for none is justified does not seem to me like the spirit of python.
Posted Dec 23, 2022 22:39 UTC (Fri)
by ballombe (subscriber, #9523)
[Link]
This seems nice, but there is a defect: suppose you now have a function fun2 calling fun:
If None was allowed, then the function could be written as
Posted Dec 25, 2022 17:50 UTC (Sun)
by caliloo (subscriber, #50055)
[Link] (14 responses)
Is actually less characters than
x=?? 42
Besides, having condition and assignment on 2 different lines fits more with the rhythm of python overall, I think.(I know, list comprehensions, lambdas…)
I often find that full time somewhat experienced programmers enter a race in the least number of lines/chars at the expense of everything else (functional form in Java for example, or Perl, or user defined ops in cpp) When you start wielding this hammer you tend to find nails naturally, because you re at a level of knowledge where typing less advantages you. But this vantage point from which the balance advantages/inconvenients is measured is not the only one. One of the strength of python is that people can be onboarded easily, or not make programming their full time job. From these other pov I’d say that the most advantageous form if the first one.
I wonder if there wouldn’t also be an advantage to explicitly make a subset of python “advanced features” enableable with a flag or some such thing. But I don’t have any personal experience in such way of doing things personally.
I’d be curious though to see if the balance of users keeps shifting towards full time programmers as it has for the past decade or more. This would reshuffle the cards on how to make that choice in my opinion.
Posted Dec 25, 2022 22:33 UTC (Sun)
by Wol (subscriber, #4433)
[Link] (12 responses)
The problem with that, is that it makes programmers an elite. Which actually leads to bad systems! We have programmers who don't know what the program is for, we have users who can't program, and we have business analysts - sorry chinese whisperers - who are trying to square the circle.
Cue a big disastrous mess ...
Cheers,
Posted Dec 26, 2022 2:04 UTC (Mon)
by NYKevin (subscriber, #129325)
[Link] (11 responses)
1. Programmers use it, business analysts don't, it starts being treated like a "real" programming language, and eventually everyone forgets that it was a "for non-programmers" language in the first place. At most, it will retain some funny-looking syntax or quirks of semantics. E.g. PHP, SQL, COBOL, BASIC.
Learning to code is challenging for many people. But it is not "the hard part" of programming. The hard part is learning to reason about the logical implications of code. Learning to boil down a user story (or whatever they're calling them these days) into a logically rigorous set of functional and non-functional requirements, and ensuring that each module of the larger whole upholds its end of the bargain. No amount of hand waving will convince the computer that it should "just do what I want." Even with AI, there will always be a conceptual gap between what you want, and what you ask for. AI is not magic. It cannot read your mind. And neither can "friendly" user interfaces.
* "Real" = "not just an educational toy like Scratch, but a proper language you might try to use for serious purposes."
Posted Dec 26, 2022 9:26 UTC (Mon)
by Wol (subscriber, #4433)
[Link]
I can name one (of course). DataBASIC. There are plenty of systems that were written in that. Pretty much the entire travel industry. HOLMES. A bit of digging could find plenty more. And there are plenty of horror stories about the disasters as people tried to migrate away from this "niche" system that was embedded everywhere that nobody knew about ...
But the problem was Dick Pick spent his money on lawyers, not marketeers, and SQL took over :-(
Cheers,
Posted Dec 26, 2022 10:19 UTC (Mon)
by caliloo (subscriber, #50055)
[Link] (1 responses)
Posted Dec 26, 2022 17:38 UTC (Mon)
by NYKevin (subscriber, #129325)
[Link]
[1]: https://thedailywtf.com/articles/The_Customer-Friendly_Sy...
Posted Dec 28, 2022 1:16 UTC (Wed)
by MrWim (subscriber, #47432)
[Link] (7 responses)
Posted Dec 28, 2022 10:07 UTC (Wed)
by Wol (subscriber, #4433)
[Link] (6 responses)
I'm a novice at VBA and the number of landmines is atrocious. Excel is not a database.
Our whole system is a mess, and I doubt it's any worse than any other big corp out there ...
A lot of my work is finding and fixing design and data errors ...
Cheers,
Posted Dec 28, 2022 14:44 UTC (Wed)
by MrWim (subscriber, #47432)
[Link] (5 responses)
Posted Dec 28, 2022 16:33 UTC (Wed)
by Wol (subscriber, #4433)
[Link] (4 responses)
The problem basically is that any use of a spreadsheet beyond the elementary is probably using it as a database - a role for which it is particularly badly suited. (You know my views on relational :-) also equally badly suited for database TECHNOLOGY :-)
Throw into the mix people who don't know how to program, and the result is a complete mess. Even if they do know how to program, you know the saying ... "if all you have is a hammer, every problem looks like a nail ..."
Cheers,
Posted Feb 25, 2023 0:17 UTC (Sat)
by nix (subscriber, #2304)
[Link] (3 responses)
The lack of major fires, explosions, and large-scale power cuts in the 1990s suggests that this crazy lash-up did in fact work.
Posted Feb 25, 2023 7:42 UTC (Sat)
by donald.buczek (subscriber, #112892)
[Link]
This is a single example and it is diffucult to research how many (if any) problems were caused by this choice.
See https://thenextweb.com/news/excel-autocorrect-errors-plag... down to "Spreadsheet catastrophes" for some counter-examples.
Posted Feb 25, 2023 11:18 UTC (Sat)
by Wol (subscriber, #4433)
[Link] (1 responses)
Written by a guy who understood programming, that sheet was probably a well designed database with a programming language. Everything needed to do a good job.
There are far better tools out there, but a decent tool in the hands of competent tool can get you far ...
The right tool in the hands of an expert, well that's another kettle of fish!
Cheers,
Posted Feb 25, 2023 12:14 UTC (Sat)
by Wol (subscriber, #4433)
[Link]
Whoops ...
decent tool in the hands of a competent expert
Cheers,
Posted Dec 26, 2022 23:56 UTC (Mon)
by pm215 (subscriber, #98099)
[Link]
Posted Dec 28, 2022 9:22 UTC (Wed)
by tialaramex (subscriber, #21167)
[Link] (6 responses)
"None-aware operators" are a workaround for lacking Option / Maybe / the "... | None" ad hoc type.
Exceptions are a workaround for lacking Result / Expect
Languages should bite the bullet and provide Sum types even though that's a lot of work.
Posted Dec 28, 2022 21:55 UTC (Wed)
by NYKevin (subscriber, #129325)
[Link] (5 responses)
The real problem is that Python is dynamic, and "supporting Sum types" doesn't mean anything in the context of dynamic languages. Well, not unless you want to build some kind of wrapper object that really exists at runtime and can't be monomorphized away. But you don't need language-level support for that, you can Just Do It.
Posted Dec 29, 2022 2:21 UTC (Thu)
by tialaramex (subscriber, #21167)
[Link] (4 responses)
Posted Dec 29, 2022 8:20 UTC (Thu)
by smurf (subscriber, #17840)
[Link] (3 responses)
There are ways to declare interfaces for that kind of code; however, if you want to find typing problems statically instead of crashing on them, Python wants you to use a separate checker (like mypy).
Posted Dec 29, 2022 13:12 UTC (Thu)
by mathstuf (subscriber, #69389)
[Link]
Posted Dec 29, 2022 20:42 UTC (Thu)
by tialaramex (subscriber, #21167)
[Link] (1 responses)
This pollution has become pretty bad in C++ where on top of about 100 reserved words like "for" and "reinterpret_cast" and "co_await" there are a vast number of in practice unavailable identifier names which, if you make the mistake of using them, implicitly mark the affected type as having specific properties associated with that word e.g. cbegin() means you're able to provide a constant iterator, size() means you're some container with things inside it. Even though C++ doesn't "Have" duck typing in this respect in practice that's what it does.
Better, I am confident, to be able to state explicitly what you mean, if this Lyre Bird can in fact smurf::animal-noises::Dog::bark that's fine, but it shouldn't be confused with my tialaramex::army::DrillSergeant::bark which is provided neither by Lyre Birds nor Dogs. We need to be clear what is meant.
core::cmp::Ordering and core::sync::atomic::Ordering are both really, really important, but in their own context. Just because my sort method needs an Ordering and your spinlock code has an Ordering doesn't mean they're concerned with the same thing. If the two are in adjacent code, that code needs to spell out what's going on. In unrelated code, the expectation can be stated just once and then context is clear afterwards.
Posted Dec 29, 2022 21:56 UTC (Thu)
by mathstuf (subscriber, #69389)
[Link]
The mechanism of templating in C++ basically feels like it (since it feels a lot like "structured preprocessor" more than "provides interface"). Concepts should help, but standard library usage remains to be seen.
Posted Dec 29, 2022 16:01 UTC (Thu)
by NRArnot (subscriber, #3033)
[Link]
In all contexts you can do
I for one don't find the case for a slew of new operators overwhelming. I'm not sure they enhance code readability, and the above are not particularly verbose.
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Wol
Not coalescing around None-aware
Not coalescing around None-aware
Wol
Not coalescing around None-aware
That'd be FORTRAN IV, released in 1962...
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
>
> data = data ?? []
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Wol
Not coalescing around None-aware
Not coalescing around None-aware
Wol
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
$ python3
Python 3.9.2 (default, Feb 28 2021, 17:03:44)
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> π = 3.1415
>>> print(π)
3.1415
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
The first is about how easy it is to understand what an expression means/does, how much other knowledge is needed (language, construct, API, ...).
The second is how much time I need to parse the expression in my head.
"data = data ?? []" is compact but not expressive.
Not coalescing around None-aware
Not coalescing around None-aware
It's immediately clear to the reader that this is probably iterating over an array of some kind and i is the index.
Special syntax always increases the mental load and increases the initial learning effort. (Saying that people don't have to lean ?? is also wrong, because other people will use it in their code and examples that Newbies will read).
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
"verbosity" is not about "how easy it is to read something", it's about the number of tokens/words needed to say something.
Then based on your familiarity with a given expression, the verbosity will have a varying impact on your parsing/understanding speed.
Familiarity will make things more readable in all cases, either by making things appear more compact/less verbose by skipping some parts, or by making them more expressive as it gets more ingrained.
Not coalescing around None-aware
> Familiarity will make things more readable in all cases,...
On the contrary, Not coalescing around None-aware
?.
and ?[]
would be useful and self-evident additions, while ??
isn't obvious or beautiful.
Not coalescing around None-aware
* Have unit tests which pass None. This requires additional work, since you need to cover all possible code paths.
Not coalescing around None-aware
Not coalescing around None-aware
if x is None:
x = 42
is already pretty readable and concise, and the only problem people seem to have with it is that it is required "too often" right?
So IMO it could be solved by looking at all the situations where this is required and improve the language so the most common use-case for checking for None isn't even needed. In fact python already have things that do this optimization, imagine how annoying this None check problem would be if python didn't have foo(arg=Default) for optional arguments. So introducing similar features could eliminate this None problem and probably do more good to readability than any possible operator name people can think of.
Not coalescing around None-aware
if foo is None
are code smell: a sign of poorly created API (perhaps unavoidable, because introduced from an external library). Similarly to the checked exceptions, when you start to look for improved syntax which would get rid of if is None
then reconsider whole code around the problem and you may find that you need restructure it to express your algorithm better.Not coalescing around None-aware
l = []
x2 = X()
print(x1.l, x2.l)
x1.l.append(1)
print(x1.l, x2.l)
[1] [1]
Not coalescing around None-aware
Not coalescing around None-aware
if check_size_limits and (len(message.header ?? "") +
len(message.body ?? "")) > limit:
raise Exception()
if check_size_limits:
header_length = 0
if message.header is not None:
header_length = len(message.header)
body_length = 0
if message.body is not None:
body_length = len(message.body)
if header_length + body_length > limit:
raise Exception()
if check_size_limits and ((len(message.header) if message.header is not None else 0) +
(len(message.body) if message.body is not None else 0)) > limit:
raise Exception()
Not coalescing around None-aware
Not coalescing around None-aware
(override ?? fallback).name ?? default
coalesce(coalesce(override, fallback).name, default
@
and dot()
instead. (It got accepted.)Not coalescing around None-aware
Not coalescing around None-aware
That's a perfect example of why we shouldn't have this operator.
Seriously. Why don't you simply resolve the None-defaults before doing the calculation?
Even with the ?? operator it would be much cleaner to pull it out of the calculation expression.
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
2. You can't modify the semantics of the existing syntax, both because it would break backwards compatibility, and also because there is an actual use case for eager evaluation of default values (namely, forcing a closure to eagerly evaluate its closed-over variables) and so you would need to provide an alternative for that. The total amount of ugliness in the language would probably increase.
Not coalescing around None-aware
Not coalescing around None-aware
lazy = []
eager = []
for i in range(5):
lazy.append(lambda: i)
eager.append(lambda x=i: x)
print(lazy[2]()) # Prints 4
print(eager[2]()) # Prints 2
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
a.getpath('foo/bar/baz')
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
You can do
fun(a,b=foo(a)) = { ... }
and foo(a) is only evaluated if b is not given,
that is, fun(A) will lead to foo(A) being evaluated but not fun(A,B).
fun2(c,a,b) = { ... fun(a,b) ... }
then b become mandatory in fun2. The way to have b optional is to write
fun2(c,a,b=foo(a)) = { ...; fun(a,b); ... }
repeating the code to compute the default value.
fun2(c,a,b=None) = { ...; fun(a,b); ... }
Not coalescing around None-aware
If x is none:
x=42
x = 42 if x is none else x
Is of course shorter, but readability will depend on your proficiency in python operators, which implies a greater prerequisite on your knowledge of python.
Not coalescing around None-aware
Wol
Not coalescing around None-aware
2. Programmers use it, business analysts don't, but everyone pretends otherwise and insists on keeping it "friendly" to non-programmers, to the detriment of the system's usability. At best it becomes a niche system used by one or two very large organizations for some highly specialized purpose. Regardless, nobody else will touch it with a ten foot pole. I don't have examples, because these things are usually proprietary and nearly always NDA'd out the wazoo. GUI interfaces are common. Branching may be represented using flow charts. You may have to construct individual lines of code by picking instructions and operands out of drop-down boxes. Etc.
Not coalescing around None-aware
Wol
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Wol
Not coalescing around None-aware
Not coalescing around None-aware
Wol
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Wol
Not coalescing around None-aware
Wol
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
Not coalescing around None-aware
In almost all contexts you can do Not coalescing around None-aware
x = x or default
the exception being where x may have a falsy value which is not None and which you do not want to replace.
( default if x is None else x)