Skip to content

Conversation

@cdce8p
Copy link
Contributor

@cdce8p cdce8p commented Jan 31, 2026

Basic requirements (all PEP Types)

  • Read and followed PEP 1 & PEP 12
  • File created from the latest PEP template
  • PEP has next available number, & set in filename (pep-NNNN.rst), PR title (PEP 123: <Title of PEP>) and PEP header
  • Title clearly, accurately and concisely describes the content in 79 characters or less
  • Core dev/PEP editor listed as Author or Sponsor, and formally confirmed their approval
  • Author, Status (Draft), Type and Created headers filled out correctly
  • PEP-Delegate, Topic, Requires and Replaces headers completed if appropriate
  • Required sections included
    • Abstract (first section)
    • Copyright (last section; exact wording from template required)
  • Code is well-formatted (PEP 7/PEP 8) and is in code blocks, with the right lexer names if non-Python
  • PEP builds with no warnings, pre-commit checks pass and content displays as intended in the rendered HTML
  • Authors/sponsor added to .github/CODEOWNERS for the PEP

Standards Track requirements

  • PEP topic discussed in a suitable venue with general agreement that a PEP is appropriate
  • Suggested sections included (unless not applicable)
    • Motivation
    • Rationale
    • Specification
    • Backwards Compatibility
    • Security Implications
    • How to Teach This
    • Reference Implementation
    • Rejected Ideas
    • Open Issues
  • Python-Version set to valid (pre-beta) future Python version, if relevant
  • Any project stated in the PEP as supporting/endorsing/benefiting from the PEP formally confirmed such
  • Right before or after initial merging, PEP discussion thread created and linked to in Discussions-To and Post-History

📚 Documentation preview 📚: https://pep-previews--4799.org.readthedocs.build/

@cdce8p cdce8p requested a review from a team as a code owner January 31, 2026 17:06
@cdce8p cdce8p marked this pull request as draft January 31, 2026 17:06
Copy link

@nschneid nschneid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a few typos

@hugovk
Copy link
Member

hugovk commented Jan 31, 2026

@gvanrossum And please can you confirm sponsorship of this as well as #4798?

@gvanrossum
Copy link
Member

@gvanrossum And please can you confirm sponsorship of this as well as #4798?

Yes of course.

Copy link
Member

@gvanrossum gvanrossum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I love that these get a serious treatment and I hope we can get the PEP to make it into 3.15. Again I have some editorial suggestions (some of which are generic and could apply to 823 as well) and some grammar nits and typos.

This PEP proposes adding two new operators.

* The "``None`` coalescing" operator ``??``
* The "Coalescing assignment" operator ``??=``
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we twist our mind so the operator names are more similar? I'd like both to be known as None-coalescing or both as coalescing. (And honestly I don't know what "coalescing" means, really, except for this precise case.)

In JavaScript it appear they are called the nullish coalescing operator (??) and the nullish coalescing assignment operator (??=). (Null-ish because they treat undefined and null the same way. But no other falsy values.)

For C# I see: The ?? and ??= operators are the C# null-coalescing operators.


The coalescing operators are **opt-in**. Existing programs will
continue to run as is. So far code which used either ``??`` or ``??=``
raised a ``SyntaxError``.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just state that ? is not allowed by the current Python grammar? (Also for 823 -- there's an annoying amount of duplication between the two PEPs that makes me think that maybe they ought to be coalesced again. ;-)

Which makes me think that maybe we should look if there are 3rd party macro systems for Python that do use ? ?

Security Implications
=====================

There are no new security implications from this proposal.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except for applications reserving ? or ?? for themselves perhaps?

Comment on lines 302 to 306
In a practical sense it might be helpful to think of the "``None``
coalescing" operator ``??`` as a special case for the conditional ``or``
operator, with the caveat that ``??`` checks for ``is not None`` instead
of truthiness. As such it makes sense to include ``??`` when teaching
about the other conditional operators ``and`` and ``or``.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed! It's super simple to explain. (Except that the shortcut behavior of or and the idioms around it are arguably a somewhat advanced topic.)

Copy link
Contributor

@Hnasar Hnasar Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these advanced usage distinctions are important to cover in the documentation. Choosing between or and ?? is inherently context-dependent, and clear guidance on when None is appropriate — versus when a neutral default leads to a cleaner API — would be valuable.

Documenting this distinction helps ensure ?? is most useful without than encouraging broader reliance on None. This was a key concern raised during discussion of pep 505, so addressing it directly would strengthen the proposal.

For example:

While None is a convenient way to signal absence, it is not always the best default When a type has a natural identity or neutral value that already represents an "empty" state, introducing None can add unnecessary branching and complexity.
In such cases, using that neutral value and relying on truthiness with or often leads to simpler, more idiomatic code:

def show_pet_name(name: str = ""):
    print("Pet name:", name or random_name())

Here, the empty string is a reasonable default because it clearly represents "no name provided."

In contrast, sometimes every value of a type — including its neutral value — is semantically meaningful. In those cases, None serves as an explicit sentinel representing absence. This is where the None-coalescing operators are more appropriate:

def set_room_temperature(temperature: int | None = None):
    print("Setting thermostat to", temperature ?? calculate_regional_default())

In this example, 0 could be a valid temperature setting, so using or would be incorrect; ?? preserves the distinction between an explicit value and no value at all.

Comment on lines +308 to +310
The "coalescing assignment" ``??=`` operator can be best thought of as
a conditional assignment operator. As it is closely related to ``??``
explaining these together would make sense.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say x ??= y is to x ?? y as x += y is to x + y. IOW, x ??= y means x = x ?? y. No more needs to be said.

Comment on lines +317 to +318
https://github.com/cdce8p/cpython/tree/pep-XXX. A online demo can be
tested at https://pepXXX-demo.pages.dev/.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, these 404 for me.

Copy link
Contributor

@Hnasar Hnasar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for writing this up in such a clear way. Exciting to see this move forward!

Comment on lines 427 to 442
One of the reasons why :pep:`505` stalled was that some expressed their
concern how coalescing and ``None``-aware operators will affect the code
written by developers. If it is easier to work with ``None`` values,
this will encourage developers to use them more. They believe that
e.g. returning an optional ``None`` value from a function is usually an
anti-pattern. In an ideal world the use of ``None`` would be limited as
much as possible, for example with early data validation.

It is certainly true that new language features affect how the language
as a whole develops. Therefore any changes should be considered carefully.
However, just because ``None`` represents an anti-pattern for some, has
not prevented the community as a whole from using it extensively. Rather
the lack of coalescing operators has stopped developers from writing
concise expressions and instead often leads to more complex code or such
which can contain subtle errors, see the `Motivation`_ section for more
details.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Some of this concern has also been raised by the original author of PEP 505 e.g. in 2021 and again in 2025)

suggestion: Expanding the discussion of this viewpoint would strengthen the proposal. A more thorough treatment of the risks and trade-offs around encouraging None-centric APIs would make the case more balanced and persuasive.

(For context on my own bias: I’ve grown to appreciate None-free API design over time, though I do think ?? can be valuable in the right situations. My hope is that this PEP clearly steers usage toward those situations and away from patterns that could lead to subtle misuse.)

Suggested change
One of the reasons why :pep:`505` stalled was that some expressed their
concern how coalescing and ``None``-aware operators will affect the code
written by developers. If it is easier to work with ``None`` values,
this will encourage developers to use them more. They believe that
e.g. returning an optional ``None`` value from a function is usually an
anti-pattern. In an ideal world the use of ``None`` would be limited as
much as possible, for example with early data validation.
It is certainly true that new language features affect how the language
as a whole develops. Therefore any changes should be considered carefully.
However, just because ``None`` represents an anti-pattern for some, has
not prevented the community as a whole from using it extensively. Rather
the lack of coalescing operators has stopped developers from writing
concise expressions and instead often leads to more complex code or such
which can contain subtle errors, see the `Motivation`_ section for more
details.
One reason :pep:`505` ultimately stalled was concern that introducing
``None``-coalescing operators would normalize and thereby encourage
``None``-centric APIs, rather than promoting ``None``-free interface
designs. Such designs favor well-defined identity or neutral objects
(e.g. ``0`` and ``""``) or, alternatively, surface failure through exceptions.
It is certainly true that new language features affect how the language
and its idioms evolve, and this concern deserves careful consideration.
While ``None`` can be the most appropriate representation of absence in
some cases, it is also widely used today in situations where a neutral
value or a different design might lead to clearer APIs. The lack of
coalescing operators can make such code more verbose, but adding them
also risks making ``None`` the path of least resistance.
To address this, as noted in the “How to Teach This” section, the
documentation should explicitly discuss when ``None`` and coalescing
operators are appropriate and when a neutral default or alternative
design is preferable.

On the the 'subtle errors' point: sometimes None can prevent bugs, but other times, introduce them by allowing neutral values which should be invalid. For example, I've seen a real-world outage caused by this type of logic bug, which is more likely with None-coalescing operators (and would be avoided with or):

  def remove_cache(path: str | None):
-     path or expensive_function_to_find_cache()
+     path ??= expensive_function_to_find_cache()
      shutil.rmtree(Path(path))

With such a change, if this function is called as remove_cache(""), the empty string is not None, so the fallback is skipped and the current working directory may be removed (I have unfortunately seen this exact bug). Code written with or would behave differently here, which illustrates why guidance on choosing between falsey defaults and None is important.

Comment on lines 302 to 306
In a practical sense it might be helpful to think of the "``None``
coalescing" operator ``??`` as a special case for the conditional ``or``
operator, with the caveat that ``??`` checks for ``is not None`` instead
of truthiness. As such it makes sense to include ``??`` when teaching
about the other conditional operators ``and`` and ``or``.
Copy link
Contributor

@Hnasar Hnasar Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these advanced usage distinctions are important to cover in the documentation. Choosing between or and ?? is inherently context-dependent, and clear guidance on when None is appropriate — versus when a neutral default leads to a cleaner API — would be valuable.

Documenting this distinction helps ensure ?? is most useful without than encouraging broader reliance on None. This was a key concern raised during discussion of pep 505, so addressing it directly would strengthen the proposal.

For example:

While None is a convenient way to signal absence, it is not always the best default When a type has a natural identity or neutral value that already represents an "empty" state, introducing None can add unnecessary branching and complexity.
In such cases, using that neutral value and relying on truthiness with or often leads to simpler, more idiomatic code:

def show_pet_name(name: str = ""):
    print("Pet name:", name or random_name())

Here, the empty string is a reasonable default because it clearly represents "no name provided."

In contrast, sometimes every value of a type — including its neutral value — is semantically meaningful. In those cases, None serves as an explicit sentinel representing absence. This is where the None-coalescing operators are more appropriate:

def set_room_temperature(temperature: int | None = None):
    print("Setting thermostat to", temperature ?? calculate_regional_default())

In this example, 0 could be a valid temperature setting, so using or would be incorrect; ?? preserves the distinction between an explicit value and no value at all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants