Thanks everyone for the feedback and discussion so far. I want to address some of the themes, so apologies for not quoting individuals and for doing this in one post instead of twenty.

------


* "It looks like line noise"

Thanks for the feedback. There's nothing constructive for me to take from this.

* "I've never needed this"

Also not very actionable, but as background I'll say that this was exactly my argument against adding them to C#. But my coding style has adapted to suit (for example, I'm more likely to use "null" as a default value and have a single function implementation than two mostly-duplicated overloads).

* "It makes it more complex"
* "It's harder to follow the flow"

Depends on your measure of complexity. For me, I prioritise "area under the indentation" as my preferred complexity metric (more lines*indents == more complex), as well as left-to-right reading of each line (more random access == more complex). By these measures, ?. significantly reduces the complexity over any of the current or future alternatives::

def f(a=None):
    name = 'default'
    if a is not None:
        user = a.get_user()
        if user is not None:
            name = user.name
    print(name)

def f(a=None):
    if a is not None:
        user = a.get_user()
        name = user.name if user is not None else 'default'
        print(name)
    else
        print('default')

def f(a=None):
    user = a.get_user() if a is not None else None
    name = user.name if user is not None else 'default'
    print(name)

def f(a=None):
    print(user.name
          if (user := a.get_user() if a is not None else None) is not None
          else 'default')

def f(a=None):
    print(a?.get_user()?.name ?? 'none')

* "We have 'or', we don't need '??'"

Nearly-agreed, but I think the tighter binding on ?? makes it more valuable and tighter test make it valuable in place of 'or'.

For example, compare:

    a ** b() or 2 # actual:   (a ** b()) or 2
    a ** b() ?? 2 # proposed:  a ** (b() ?? 2)

In the first, the presence of 'or' implies that either b() or __pow__(a, b()) could return a non-True value. This is correct (it could return 0 if a == 0). And the current precedence results in the result of __pow__ being used for the check.

In the second one, the presence of the '??' implies that either b() or __pow__(a, b()) could return None. The latter should never happen, and so the choices are to make the built-in types propagate Nones when passed None (uhh... no) or to make '??' bind to the closer part of the expression.

(If you don't think it's likely enough that a function could return [float, None], then assume 'a ** b?.c ?? 2' instead.)

* "We could have '||', we don't need '??'"

Perhaps, though this is basically just choosing the bikeshed colour. In the absence of a stronger argument, matching existing languages equivalent operators instead of operators that do different things in those languages should win.

* "We could have 'else', we don't need '??'"

This is the "a else 'default'" rather than "a ?? 'default'" proposal, which I do like the look of, but I think it will simultaneously mess with operator precedence and also force me to search for the 'if' that we actually need to be comparing "(a else 'default')" vs. "a ?? 'default'"::

    x = a if b else c else d
    x = a if (b else c) else d
    x = a if b else (c else d)

* "It's not clear whether it's 'is not None' or 'hasattr' checks"

I'm totally sympathetic to this. Ultimately, like everything else, this is a concept that has to be taught/learned rather than known intrinsically.

The main reasons for not having 'a?.b' be directly equivalent to getattr(a, 'b', ???) is that you lose the easy ability to find typos, and we also already have the getattr() approach.

(Aside: in this context, why should the result be 'None' if an attribute is missing? For None, the None value propagates (getattr(a, 'b', a)), while for falsies you could argue the same thing applies. But for a silently handled AttributeError? You still have to make the case that None is special here, just special as a return value vs. special as a test.)

* "The semantics of this example changed from getattr() with ?."

Yes, this was a poor example. On re-reading, all of the checks are indeed looking for optional attributes, rather than looking them up on optional targets. I'll find a better one (I've certainly seen and/or written code like this that was intended to avoid crashing on None, but I stopped my search of the stdlib too soon after finding this example).

* "Bitwise operators"

Uh... yeah. Have fun over there :)

* "Assumes the only falsie ever returned [in some context] is None"

I argue that it assumes the only falsie you want to replace with a different value is None. In many cases, I'd expect the None to be replaced with a falsie of the intended type:

    x = maybe_get_int() ?? 0
    y = maybe_get_list() ?? []

Particularly for the second case, if you are about to mutate the list, then it could be very important that you don't replace the provided reference with your own, just because it happens to be empty right now (this is the logging example in the PEP). Supporting a default value in the get() calls is obviously a better way to do this though, provided you have the ability to modify the API.

But for me, the ?? operator makes its strongest case when you are getting attributes from potentially None values:

    x = maybe_get()?.get_int() ?? 0
    y = maybe_get()?.get_list() ?? []

In this case, using 'or' may replace an intentionally falsie value with your own, while using a default parameter is still going to leave you with None if the first maybe_get() returned nothing.

"?." without "??" feels like a trap, while "??" without "?." feels largely unnecessary. But both together lets you turn many lines of code into a much shorter snippet that reads left-to-right and lets you assume success until you reach the "??". That, for me, is peak readability.

_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to