On Sun, Dec 5, 2021 at 6:16 AM Brendan Barnwell <brenbarn@brenbarn.net> wrote:
On 2021-12-04 03:50, Chris Angelico wrote:
On Sat, Dec 4, 2021 at 8:48 PM Steven D'Aprano <steve@pearwood.info> wrote:
And third, when the interpreter fetches a default from func.__defaults__, if it is a LB function, it automatically calls that function with the parameters to the left of x (which in this case would be just b).
Plausible. Okay.
What this does mean, though, is that there are "magic objects" that cannot be used like other objects. Consider:
Your proposal also has the same problem, since it involves "magic functions" that do not have usable values for their argument defaults, instead having some kind of Ellipsis two-step. It's all a matter of what you consider magic.
My proposal allows any object to be used as a function default argument. There's a minor technical difference that means that there's a second lookup if you use Ellipsis, but you can still use Ellipsis just fine.
def f(x=...): ... print(type(x), x) ... f() <class 'ellipsis'> Ellipsis f(None) <class 'NoneType'> None f("spam") <class 'str'> spam
There are no objects that will behave differently if used in this way. EVERY object can be a function default argument. Steve's proposal has some objects (functions with the LB flag set) actually behave differently - they *will not behave correctly* if used in this way. This is a restriction placed on the rest of the language.
Great. So now we have some magnificently magical behaviour in the language, which will have some nice sharp edge cases, but which nobody will ever notice. Totally. I'm sure. Plus, we pay a performance price in any function that makes use of argument references, not just for the late-bound default, but in the rest of the code. We also need to have these special functions that get stored as separate code objects.
All to buy what, exactly? The ability to manually synthesize an equivalent parameter value, as long as there's no assignment expressions, no mutation, no other interactions, etc, etc, etc? That's an awful lot of magic for not a lot of benefit.
I would consider most of what you say here an accurate description of your own proposal. :-)
That's highly unfair. No, I won't let that pass. Please retract or justify that statement. You are quoting the conclusion of a lengthy post in which I show significant magic in Steve's proposal, contrasting it with mine which has much clearer behaviour, and you then say that my proposal has the same magic. Frankly, that is not a reasonable assertion, and I take offense.
Now we have magnificently magical behavior in the language, which will take expressions in the function signature and behind the scenes "inline" them into the function body. We also need to have these special function arguments that do NOT get stored as separate objects, unlike ordinary function arguments. All to buy what, exactly? The ability to write something in the function signature that we can already write in the body, and that quite naturally belongs in the body, because it is executed when the function is called, not when it is defined.
You assert that it "belongs in the body", but only because Python currently doesn't allow it to be anywhere else. Other languages have this exact information in the function signature. This is a much larger distinction than what Steve shows, which is the exact same feature but with these magic callables.
I *really* don't like the idea that some types of object will be executed instead of being used, just because they have a flag set. That strikes me as the sort of thing that should be incredibly scary, but since I can't think of any specific reasons, I just have to call it "extremely off-putting".
I *really* don't like the idea that some types of argument will be inlined into the function body instead of being stored as first-class values like other `__defaults__`, just because there happens to be this one extra character next to the equals sign in the function signature. That strikes me as the sort of thing that should be incredibly scary.
You're still being highly offensive here. There's a HUGE difference between these two assertions. Steve's proposal makes some objects *behave differently when used in existing features*. It would be like creating a new type of string which, when printed out, would eval itself. That proposal wouldn't fly, and it's why f-strings are most assuredly NOT first-class objects. Why is it such a big deal for these function default expressions to not be first-class objects? None of these are first-class either: print(f"An f-string's {x+y} subexpressions") print(x/y if y else "An if/else expression's sides") assign(x.y[42], "An assignment target") We don't have a problem with these being unable to be externally referenced, manipulated, etc, as first-class objects. Why is it a problem to be unable to refer to "new empty list" as some sort of object when used like this? def f(x=>[]): ... Can you explain why it is necessary? And then, after that, explain why you claim that Steve's proposal, which makes some objects *not even work in early-bound defaults*, is just as magical? ChrisA