Spun off from PEP 505 thread on python-dev.

One of the weaker use-cases for None-coalescing is function default
arguments, where the technical default is None but the function wants
to use some other value instead. This is a weak argument in favour of
None-coalescing because only a small set of such situations are only
slightly improved by a "??" operator, and the problem is far larger.
Consider the bisect.bisect() function [1]:

def bisect(a, x, lo=0, hi=None):
    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi is None:
        hi = len(a)

It's clear what value lo gets if you omit it. It's less clear what hi
gets. And the situation only gets uglier if None is a valid argument,
and a unique sentinel is needed; this standard idiom makes help()
rather unhelpful:

_missing = object()
def spaminate(thing, count=_missing):
    if count is _missing: count = thing.getdefault()

Proposal: Proper syntax and support for late-bound argument defaults.

def spaminate(thing, count=:thing.getdefault()):
    ...

def bisect(a, x, lo=0, hi=:len(a)):
    if lo < 0:
        raise ValueError('lo must be non-negative')

This would be approximately equivalent to the _missing idiom, with the
following exceptions:

1) Inspecting the function would reveal the source code for the late-bound value
2) There is no value which can be passed as an argument to achieve the
same effect
3) All late-bound defaults would be evaluated before any other part of
the function executes (ie the "if hi is None" check would now be done
prior to "if lo < 0").

The syntax I've chosen is deliberately subtle, since - in many many
cases - it won't make any difference whether the argument is early or
late bound, so they should look similar. While it is visually similar
to the inline assignment operator, neither form is currently valid in
a function's parameter list, and thus should not introduce ambiguity.

The expression would be evaluated in the function's context, having
available to it everything that the function has. Notably, this is NOT
the same as the context of the function definition, but this is only
rarely going to be significant (eg class methods where a bare name in
an early-bound argument default would come from class scope, but the
same bare name would come from local scope if late-bound).

The purpose of this change is to have the function header define, as
fully as possible, the function's arguments. Burying part of that
definition inside the function is arbitrary and unnecessary.

ChrisA

[1] Technically it's bisect_right but I'm using the simpler name here.
Also, it recently grew a key parameter, which would only get in the
way here. https://docs.python.org/3/library/bisect.html#bisect.bisect_right
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/LWWVXBXNAUVANWGKAJDGBWQFDPSPQU4E/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to