
On Sun, Oct 31, 2021 at 3:08 PM Brendan Barnwell <brenbarn@brenbarn.net> wrote:
On 2021-10-30 20:44, Chris Angelico wrote:
The default of every argument should be a first-class value. That's how things are now, and I think that's a very useful invariant to have. If we want to break it we need a lot more justification than "I don't like typing if x is None".
How important is this? Yes, it's the status quo, but if we evaluate every proposal by how closely it retains the status quo, nothing would ever be changed. Let's look at type checking for a moment, and compare a few similar functions: <snip>
As I've said before, the problem is that the benefit of this feature is too small to justify this large change to the status quo.
You use similarly mysterious "expressions" all the time. They can't be treated as first-class values, but they can be used in the contexts they are in. This is exactly the same. You've probably even used expressions that are only evaluated conditionally:
recip = 1 / x if x else 0
Is the "1 / x" a first-class value here? No - and that's why if/else is a compiler construct rather than a function.
No, but that example illustrates why: the expression there is not a first-class value, but that's fine because it also has no independent status. The expression as a whole is evaluated and the expression as a whole has a value and that is what you work with. You don't get to somehow stash away just the 1/x part and use it later, but without evaluating it yet. That is not even close to the situation envisioned in the proposal under discussion, because in the proposal this mysterious expression (the argument default), although not a first-class value, is going to be stored and used independently, yet without evaluating it and without "reifying" it into a function or other such object
It's actually the same. There's no "stashing away" of the expression part; it's just part of the function, same as everything else is. (I intend to have a string representation of it stashed away, but that IS a first-class value - a str object, or a PyUnicode if you look in the C API.) You can't use that later in any way other than by calling the function while not passing it the corresponding argument. You cannot use it independently, and you cannot retrieve an unevaluated version of it.
It's obvious that there are tons of things that aren't first-class values in Python (and in any language). The plus sign isn't a first class value, the sequence of characters `a = "this".cou` in source code isn't a first class value. But these usages aren't the same as what's in the proposal under discussion here. I'm not sure if you actually disagree with this or if you're just being disingenuous with your examples.
Right. Some things are simply invalid, others are compiler constructs. I'm pointing out that compiler constructs are very real things, despite not being values. I'm not sure about your ".cou" example; the only reason that isn't a first-class value is that, when you try to look it up, you'll get an error. Or are you saying that an assignment statement isn't a value? If so, then it's the same thing again: a compiler construct, a syntactic feature. In general, syntactic features fall into three broad categories: 1) Literals, which have clear values 2) Expressions, which will yield values when evaluated 3) Everything else. Mostly statements. No value ever. For instance, the notation >> 42j << is a complex literal (an imaginary number), and >> a+b << is an expression representing a sum. Thanks to constant folding, we can pretend that >> 3+4j << is a literal too, although technically it's an expression. Argument defaults are *always* expressions. They can be evaluated at function definition time, or - if PEP 671 is accepted - at function call time. The expression itself is nothing more than a compiler construct, and isn't a first-class value. If it's early-bound, then it'll be evaluated at def time to yield a value which then gets saved on the function object and then gets assigned any time the parameter has no value; if it's late-bound, then any time the parameter has no value, it'll be evaluated at call time, to yield a value which then gets assigned to the parameter. Either way, the same steps happen, just in a different order.
Anyway, I appreciate your engaging with me in this discussion, especially since I'm just some random guy whose opinion is not of major importance. :-) But I think we're both kind of just repeating ourselves at this point. I acknowledge that your proposal is essentially clear and is aimed at serving a genuine use case, but I still see it as involving too much of a departure from existing conventions, too much hairiness in the details, and too little real benefit, to justify the change.
I think everyone's opinion is of major importance here, because you're taking the time to discuss the feature. I could go on a long rant about this, but the short version is: neither representative democracy nor pure democracy is nearly as good as a system where those in charge (here, the PSF) can get informed debate from that specific subset of people who actually care about something :) And it's fine for you to believe that this shouldn't happen. As long as the debate is respectful, courteous, professional, and based on facts, not people ("this idea sucks because YOU thought of it"), it's worth having! I'm happy to continue answering questions or countering arguments, because I believe that this IS of more value than its costs; but I'm also open to being proven wrong on that point (and believe you me, the work of implementing it showed me that the cost isn't all where I thought it would be). ChrisA