Re: PEP 671 (late-bound arg defaults), next round of discussion!

On Fri, Dec 10, 2021 at 7:54 PM Eric V. Smith <eric@trueblade.com> wrote:
Not sure if you meant this to go to the list or not.
I did — ( I know it seems to be the consensus, but I really think respond to list should be the default…) Moving on… On 12/10/2021 7:50 PM, Christopher Barker wrote:
Ahh, then that would make them less useful for default parameters— even for the dataclass case. It would work fine if you used only builtins (or literals) but that’s about it. So Chris’ canonical example of n => len(input_list) wouldn’t work. Which is why I think that if there were a general purpose deferred object, we’d need special syntax or rules for when used as late-bound defaults. I'll admit I haven't thought all of this through in enough detail to
implement it. I'm just trying to point out that we could use the general concept in other places.
Which is exactly what I asked for — thanks! I think this is a really interesting use case, not so much as it’s a general purpose deferred object, but because it’s about how to do/use late-bound defaults in meta programming.
Which would be a potentially useful deferred object, but not a good one for late-bound defaults. Here’s a new (not well thought out) idea: @dataclasses.dataclass class A: Input_list: list length: int => len(input_list) So length gets set to a “late bound default expression” that Is an actual value. It would have to store the expression itself, probably as a string, and it would get evaluated in the namespace of the function, when the function was called. So the generated __init__ would be the same as: def __init__(self, input_list, length=>len(input_list): And you could also do: get_length => len(input_list) @dataclasses.dataclass class A: Input_list: list length: int = get_length Not that that would be a good idea. This suggestion would mean that “=>” would create a deferred expression, but it would not be a general purpose one. Though maybe there could be a way to evaluate it in a more general way, kind of like eval — where you can control the namespaces used. I guess what I’m suggesting is that we could create a very specific kind of deferred object, and in the future expand it to more general use. Rather than the general case rendering the late-bound default concept. -CHB
-- Christopher Barker, PhD (Chris)
Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython

On Sun, Dec 12, 2021 at 4:06 AM Christopher Barker <pythonchb@gmail.com> wrote:
If I'm not mistaken, dataclasses generate __init__ using exec anyway, so this would work with a plain string. ChrisA

Christopher Barker writes:
This suggestion would mean that “=>” would create a deferred expression, but it would not be a general purpose one.
I would prefer that "=>" create a general purpose evaluation-deferred expression, but that the general-purpose one be tuned such that you don't need new syntax to dereference it in the common use cases. This generality might be implemented by a "context" attribute on the object, or as Eric suggests it might be a rule that "deferred objects" that are defined as default function arguments get evaluated before the function body (ie, as in PEP 671, I think).
Though maybe there could be a way to evaluate it in a more general way, kind of like eval — where you can control the namespaces used.
There is some such facility in Common Lisp, so I think this is quite feasible. There will be a lot of bikeshedding to be done about *which* namespaces deserve the simplest syntax in *which* contexts. Eg, I don't think there will be much disagreement about evaluating "deferred objects" used as argument defaults just before the function body is entered using the namespaces used in PEP 671, but the case of an actual argument that is a "deferred object" probably would be more contentious.
I don't think this works very well. At the very least, some effort should go into thinking about potential generalization and reserving some names in the class namespace so that all the good names don't get taken by early user-derived classes. I'm not sure this is a good example, but consider the case of Decimal which has a lot of good properties from the point of view of "naive" user expectations about numerical computations. Unfortunately it was late enough to the party that it's not vary attractive unless you actually grok floating point; newbies are just going to use floats. Of course Decimal has all the same kind of traps as float, but they're far more familiar. People expect 1/3 to result in an approximately equal value; they do not expect 1/5 to do so. I wonder if making Decimal the default for decimal literals wouldn't be a good deal, but I guess I'm gonna have to wonder forever. :-) [1] Footnotes: [1] *Not* intended as a suggestion.

On Sun, Dec 12, 2021 at 4:06 AM Christopher Barker <pythonchb@gmail.com> wrote:
If I'm not mistaken, dataclasses generate __init__ using exec anyway, so this would work with a plain string. ChrisA

Christopher Barker writes:
This suggestion would mean that “=>” would create a deferred expression, but it would not be a general purpose one.
I would prefer that "=>" create a general purpose evaluation-deferred expression, but that the general-purpose one be tuned such that you don't need new syntax to dereference it in the common use cases. This generality might be implemented by a "context" attribute on the object, or as Eric suggests it might be a rule that "deferred objects" that are defined as default function arguments get evaluated before the function body (ie, as in PEP 671, I think).
Though maybe there could be a way to evaluate it in a more general way, kind of like eval — where you can control the namespaces used.
There is some such facility in Common Lisp, so I think this is quite feasible. There will be a lot of bikeshedding to be done about *which* namespaces deserve the simplest syntax in *which* contexts. Eg, I don't think there will be much disagreement about evaluating "deferred objects" used as argument defaults just before the function body is entered using the namespaces used in PEP 671, but the case of an actual argument that is a "deferred object" probably would be more contentious.
I don't think this works very well. At the very least, some effort should go into thinking about potential generalization and reserving some names in the class namespace so that all the good names don't get taken by early user-derived classes. I'm not sure this is a good example, but consider the case of Decimal which has a lot of good properties from the point of view of "naive" user expectations about numerical computations. Unfortunately it was late enough to the party that it's not vary attractive unless you actually grok floating point; newbies are just going to use floats. Of course Decimal has all the same kind of traps as float, but they're far more familiar. People expect 1/3 to result in an approximately equal value; they do not expect 1/5 to do so. I wonder if making Decimal the default for decimal literals wouldn't be a good deal, but I guess I'm gonna have to wonder forever. :-) [1] Footnotes: [1] *Not* intended as a suggestion.
participants (4)
-
Chris Angelico
-
Christopher Barker
-
Eric V. Smith
-
Stephen J. Turnbull