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 Sun, Jun 19, 2022, 10:31 AM Chris Angelico <ros...@gmail.com> wrote:

> On Mon, 20 Jun 2022 at 00:06, David Mertz, Ph.D. <david.me...@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-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/OEINIFBLDSX5F2GF4T4EFFUBMEQIODBT/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
_______________________________________________
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/VJYXJR7ZGRLV7H2N6VFIYIDF3YUDNPZA/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to