Unfortunately, in relation to this PEP, I find your arguments tend to be sophistical. They ate not generally so in other threads, but for whatever reason your attachment to this has a different quality.
There's an interesting point you raise though. You seem to feel that closely related meanings should have similar looking sigils. I'm not sure my opinion is *opposite *, but it's definitely more that subtly different semantics should not be marked by easily visually confusable sigils.
Under PEP 671, a single line of a function signature might contain '=', ':=', '=>', '->', ':', and '=='. Obviously it can have other symbols as well. But those are the ones the most blur into each other visually. 4 of them have closely related meanings. You don't even need to be contrived to have such examples.
I guess '>=' also looks "confusable", but it's far less common in signatures, and the meaning is further away.
Below you seem to try another unconvincing reductio ad absurdum to suggest that I must either want a language with no symbols or support this PEP.
I think the cognitive complexity of a line with sigils is somewhere around quadratic or cubic on the number of distinct sigils. But when several look similar, it quickly tends toward the higher end. And when several have related meanings, it's harder still to read.
When I write an expression like 'a - b * c / d**e + f' that also has a bunch of symbols. But they are symbols that:
- look strongly distinct
- have meanings familiar from childhood
- have strongly different meanings (albeit all related to arithmetic)
Yes, I probably have to think for a while about operator precedence to make sure I understand it. Probably I'll add some redundant parens if I can edit the code. But the cognitive burden of the sigils remains FAR lower than what would occur with regularity under PEP 671.
I already dislike the walrus operator in signature context for that reason... although I think it's great for e.g. 'while a := getdata() > 0: ...'
On Mon, 20 Jun 2022 at 00:06, David Mertz, Ph.D. <david.mertz@gmail.com> wrote:
>
> On Sun, Jun 19, 2022, 3:27 AM Chris Angelico
>>
>> > I'm still -1 because I don't think the purpose alone is close to worth the cost of new syntax... And especially not using sigils that are confusing to read in code.
>> >
>> > The topic of "late binding in function signatures" simply isn't *orthogonal* to "late binding in the general sense." Yes, they are distinct, but very closely adjacent.
>>
>> Every argument you've just made is ALSO an argument against function defaults in general. Do you think that they aren't worth syntax either?
>
>
> I honestly can find no connection between what I've argued and "function defaults in general." It feels like a non sequitur, but I accept that they somehow connect in your mind.
>
Let's go through the arguments you laid out.
> It's a way to spell `if arg is sentinel: arg = ...` using slightly fewer characters, and moving an expression from the body to the signature.
>
That's what ALL argument defaults do. Some languages evaluate them
early, some evaluate them late.
> I'm still -1 because I don't think the purpose alone is close to worth the cost of new syntax...
>
What purpose? Being able to define the function's signature in its
signature, including the meaning of omitted arguments? That's
definitely the purpose of ALL default arguments, not just late-bound.
> And especially not using sigils that are confusing to read in code.
The sigils used are very similar here: "a=b" and "a=>b". Are you
really trying to tell me that "=" is not confusing to read in code,
but "=>" is, or are all such sigils equally confusing? Function
signatures already have several symbols other than those used for
defaults, including *, /, and :, not to mention ( ) and -> which
aren't inside the argument list, but are still part of the function
signature. Programmers are accustomed to working with symbols (with
the possible exception of COBOL and DeScribe Macro Language
programmers), and your argument would work just as well against any
sort of argument defaults.
> The topic of "late binding in function signatures" simply isn't *orthogonal* to "late binding in the general sense." Yes, they are distinct, but very closely adjacent.
This one, you specifically mention late binding, so that's the one exception.
Other than that, every one of your arguments could be used to show
that we shouldn't have ANY function argument defaults. Not one of them
justifies having default values without default expressions.
> Way back last Oct when this discussion had it's first round, someone (probably Steven, but maybe it was someone else) did a survey of a numerous programming languages, and whether they use early binding or late binding of default function arguments.
>
> The result was that MOST languages do late (call time) binding, albeit many of those are compiled languages where early binding doesn't really make sense. Some, like JavaScript don't have defaults at all, so the approach is actually similar to Python:
>
> const myfunc = (a, b, c) => {
> if (typeof c === 'undefined') { c = ... }
> }
Actually JavaScript does have argument defaults. (Also, I would write
this as simply "if (c === undefined)", since there's no situation in
which that would fail here.) The obvious syntax is equivalent to what
you wrote:
const myfunc = (a, b, c=...) => ...
> That's a sentinel, but worse than Python in several ways.
Not sure what you mean by "several", but it is indeed a simple
sentinel, which has the downside that you can pass the special value
undefined to the function. So I would say it's exactly equivalent to
Python and the use of None, except that it's late-bound.
> Others like Perl don't really have named arguments at all, but that weird implicit list you can pop from.
>
JavaScript, being JavaScript, has that too - which is actually good,
because it lets you distinguish between omitted arguments and those
passed as undefined.
I want to do *better* than those languages, not to use them as a pass mark.
> The one thing that there were ZERO examples of elsewhere was what you propose: slightly different syntax to allow both early-bound and late-bound. Admittedly, if Python had done late-bound from 1.0, probably no one would now have a PEP to add an early-bound spelling variant.
>
And yet early-bound argument defaults ARE of value. Maybe they
wouldn't have been worth adding syntax for, but I would be highly
confident that someone would have made a decorator for it:
@freeze_defaults(x=1)
def foo(x): ...
And, since it's hard for a decorator to remap arguments perfectly, it
would have done an imperfect job, but it might have been good enough
to not ask for the feature.
> If Python had always been late-bound, I'm sure I'd be perfectly happy with it today. But sentinels are really easy, and obscure sigils are really hard. So adding the new spelling just makes Python worse. Beginners have something needless to learn, and experienced developers have just a little more cognitive burden all the time.
>
Sentinels are only easy when you're accustomed to them AND you're
never running into the problems that they introduce. When they do,
there are many different ways around the problem, and not one of them
is perfect. Which means that those reading the code have to figure it
out piece by piece, instead of simply reading the function signature
and understanding it.
So your entire argument is "we don't have it, so we don't need it",
aside from this one small consideration, that no other language has
ever offered both options. Finding a good syntax that is sufficiently
similar to the existing "x=dflt" syntax is the key here. People should
be able to read a signature as a whole, without having to also read
several lines of decorator and/or the beginning of the function body,
and immediately know how to go about calling the function.
Python is the only language I know of that offers both eager and lazy
ways to build a list from another list using a transformation
expression. Most languages that have a map() function will give either
eager or lazy, but not both. Python lets you write a list comp or a
genexp, and the only distinction is the type of bracket used to
surround them. Is that a bad thing? No! It's a very good thing! Python
offers more power, with a very simple way to select which one you
want. Even if literally zero other languages offer that feature, it's
still of value to Python.
Python IS allowed to be the first to do something.
ChrisA
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@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/OEINIFBLDSX5F2GF4T4EFFUBMEQIODBT/
Code of Conduct: http://python.org/psf/codeofconduct/