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