On Sun, Dec 5, 2021 at 3:39 PM David Mertz, Ph.D. <david.me...@gmail.com> wrote:
>
> On Sat, Dec 4, 2021 at 11:25 PM Chris Angelico <ros...@gmail.com> wrote:
>>
>> > def add(a, b):
>> >     return a+b
>> > How could you write that differently with your PEP
>>
>> I wouldn't. There are no default arguments, and nothing needs to be changed.
>
>
> I do recognize that I *could* call that with named arguments.  I also 
> recognize that the long post I wrote in the bath from my tablet is rife with 
> embarrassing typos :-).
>
> Technically, I'd need `def add(a, b, /)` to be positional-only.  But in 
> practice, almost everyone who writes or calls a function like that passes by 
> position.  I'm not sure that I've *ever* actually used the explicit 
> positional-only `/` other than to try it out.  If I have, it was rare enough 
> that I had to look it up then, as I did just now.
>

No problem. Doesn't really matter. In any case, argument defaults have
always been orthogonal with parameter passing styles (named or
positional), and that's not changing.

>> Actually PEP 671 applies identically to arguments passed by name or
>> position, and identically to keyword-only, positional-or-keyword, and
>> positional-only parameters.
>>
>> >>> def f(a=>[], /, b=>{}, *, c=>len(a)+len(b)):
>> ...     print(a, b, c)
>
>
> Wow! That's an even bigger teaching nightmare than I envisioned in  my prior 
> post.  Nine (3x3) different kinds of parameters is already too big of a 
> cognitive burden.  Doubling that to 18 kinds makes me shudder. I admit I sort 
> of blocked out the positional-only defaults thing.
>
> I understand that it's needed to emulate some of the builtin or standard 
> library functions, but I would avoid allowing that in code review... 
> specifically because of the burden on future readers of the code.
>

What you're missing here is that it's not 3x3 becoming 3x3x2.
Everything is completely orthogonal. For instance, we don't consider
string arguments to be a fundamentally different thing from integer
arguments:

def f(text, times): ...

f("spam", 5)

They're just... arguments! And it's not massively more cognitive load
to be able to pass string arguments by name or position AND to be able
to pass integer arguments by name or position.

f(text="spam", times=5)
f("spam", times=5)

That's not a problem, because the matrix is absolutely complete: there
is no way in which these interact whatsoever.

It's the same with positional and named parameters: the way the
language assigns argument values to parameters is independent of the
types of those objects, whether they have early-bound defaults,
whether they have late-bound defaults, whether they have annotations,
and whether the function returns "Hello world". It's not quadratic
cognitive load to comprehend this. It is linear. The only thing you
need to understand is the one thing you're looking at right now.

With function defaults, it is currently the case that any parameter
can have a default, so long as there are no positional parameters to
its right which lack defaults. That's the only restriction on default
arguments. And that restriction is not changing at all by my proposal:
it is neither weakened nor strengthened by the fact that the default
might be an expression rather than a precomputed value.

(By the way, if I ever get the words "argument" and "parameter" wrong,
my apologies; but truth be told, everyone does that. The Python
grammar specifies that a def statement has a block of params, which is
of type arguments_ty. So that's a thing.)

CPython currently states that a function's parameters consist of three
groups (or five, kinda):

def func(pos_only, /, pos_or_kwd, *, kwd_only): ...
def func(pos_only, /, pos_or_kwd, *args, kwd_only, **kwargs): ...

Inside each group (not counting the collectors *args and **kwargs if
present), legality is defined by... well, I'll just quote the grammar
file:

# There are three styles:
# - No default
# - With default
# - Maybe with default
#
# There are two alternative forms of each, to deal with type comments:
# - Ends in a comma followed by an optional type comment
# - No comma, optional type comment, must be followed by close paren
# The latter form is for a final parameter without trailing comma.

And if you've never thought about type comments, don't worry, neither
had I till I was tinkering with the grammar, and we can for the most
part ignore them. :)

Either the parameter has a default, or it doesn't. A "maybe with
default" either looks like a "with default" or a "no default" (and
grammatically, it's there to handle that one restriction of "def
f(a=1, b):" being invalid, but everything else is fine). I'm not
changing any of that. All I'm changing is the default itself:

default[default_ty]:
    | '=' a=expression { _PyPegen_arg_default(p, a, DfltValue) }
    | '=' '>' a=expression { _PyPegen_arg_default(p, a, DfltExpr) }

When a parameter has a default, two options: either it's a default
value, or it's a default expression. This change doesn't care what
group of parameters it's in - and in fact, it cannot - because any
parameter can have, or not have, a default.

This is the correct way to add features. Cognitive load goes up
linearly, but expressiveness goes up polynomially. Yes, it's possible
to write all the different kinds of functions. That's great, that's
power! But you don't need to think about every possibility all at
once, in a massive N-dimensional matrix.

ChrisA
_______________________________________________
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/SOTFNE3PWSSMYCS52V32E4WNKRO4HAKZ/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to