On Sun, Oct 31, 2021 at 1:03 PM Brendan Barnwell <brenbarn@brenbarn.net> wrote:
On 2021-10-30 18:29, Chris Angelico wrote:
What I am saying is that there is a qualitative difference between "I know now (at function definition time) what value to use for this argument if it's missing" and "I know now (at function definition time) *what I will do* if this argument is missing". Specifying "what you will do" is naturally what you do inside the function. It's a process to be done later, it's logic, it's code. It is not the same as finalizing an actual VALUE at function definition time. So yes, there is a qualitative difference between:
# this if argument is undefined: argument = some_constant_value
# and this if argument is undefined: # arbitrary code here
I mean, the difference is that in one case arbitrary code is allowed! That's a big difference.
Right. That is a very real difference, which is why there is a very real difference between early-bound and late-bound defaults. But both are argument defaults.
I don't 100% agree with that.
I mean, here's another way to come at this. Suppose we have this under the proposal (using some random syntax picked at random):
def foo(a=1, b="two", c@=len(b), d@=a+c):
You keep saying that c and d "are argument default" just like a and b. So can you tell me what the argument default for each of those arguments?
The default for a is the integer 1. The default for b is the string "two". The default for c is the length of b. The default for d is the sum of a and c.
The default for argument a is an integer. The default for argument b is a string. Can you tell me, in comparable terms, what the defaults for arguments c and d are?
You're assuming that every default is a *default value*. That is the current situation, but it is by no means the only logical way a default can be defined. See above: c's default is the length of b, which is presumably an integer, and d's default is the sum of that and a, which is probably also an integer (unless a is passed as a float or something).
Currently, every argument default is a first-class value. As I understand it, your proposal breaks that assumption, and now argument defaults can be some kind of "construct" that is not a first class value, not any kind of object, just some sort of internal entity that no one can see or manipulate in any way, it just gets automatically evaluated later.
I really don't like that. One of the things I like about Python is the "everything is an object" approach under which most of the things that programmers work with, apart from a very few base syntactic constructs, are objects. Many previous expansions to the language, like decorators, context managers, the iteration protocol, etc., worked by building on this object model. But this proposal seems to diverge quite markedly from that.
What object is this? a if random.randrange(2) else b Is that an object? No; it's an expression. It's a rule. Not EVERYTHING is an object. Every *value* is an object, and that isn't changing. Or here, what value does a have? def f(): if 0: a = 1 print(a) Does it have a value? No. Is it an object? No. The lack of value is not itself a value, and there is no object that represents it.
If the "late-bound default" is not an object of some kind just like the early-bound ones are, then I can't agree that both "are argument defaults".
Then we disagree. I see them both as perfectly valid defaults - one is a default value, the other is a default expression. ChrisA