
On Sun, Oct 31, 2021 at 2:52 PM David Mertz, Ph.D. <david.mertz@gmail.com> wrote:
On Sat, Oct 30, 2021, 11:03 PM Chris Angelico
That's if you were to create a deferred *type*. An object which can be evaluated later. My proposal is NOT doing that, because there is no object that represents the unevaluated expression. You can't pull that expression out and evaluate it somewhere else. It wouldn't be meaningful.
Put this way, I admit it is intelligible. Maybe it even reduces the order of magnitude of my opposition to it.
What you are trying to create remains a "unit of computation" even though it is not itself a Python object. As Brendan points out, this is a fundamental change to Python's object model.
It amounts to saying "I want to perform some computation within the body of a function, but I don't want to WRITE it in the body of a function." That feels like an anti-goal to me.
Not quite; I want to perform it within the *context* of the function, but not in the body. A function's context (its scope, available variables, etc, etc, etc) is where most of its body is executed, but for example, a list comprehension is part of the function's body while not being in the same context (it's in a subcontext defined by a nested function).
I suppose I don't *hate* something like this, which can be done with no syntax change:
@rewrite def foo(a, size=Late("len(a)")): print("The list is length", size)
In the functions I actually use and write, the problem all of this is trying to solve is either trivial or irrelevant.
Thing is, that can't actually be done, because there's no way to make that able to see the function's parameters without some VERY weird shenanigans. You'd have to completely recreate the argument-to-parameter allocation, apply all preceding defaults, and then have a set of locals which can be passed to eval(); and then having done all that, deconstruct your locals into *a,**kw to pass back to the original function. Meanwhile, you have to capture any nonlocals, in case len isn't actually a global. In contrast, having this as a compiler construct is simple: In the context of the function, if the parameter hasn't been given a value, evaluate the expression and assign. (Side point: The current reference implementation allows assignment expressions inside default argument expressions, mainly because I didn't go to the effort of blocking them. But if you ever ACTUALLY do this, then..... *wat*)
In libraries like Pandas, for example, there are often tens of arguments with defaults of None... But it is rarely a single default to resolve. Rather, there is complex logic about the interaction of which arguments are provided or absent, leading to numerous code paths to configure the actual behavior of a call.
For these, the logic of missing arguments must live in the body, where it naturally belongs.
There will always be cases too complicated for simple default expressions, just as there are already cases too complicated for simple default values. They will continue to be handled by the body of the function, as they currently are. This proposal isn't a replacement for all argument processing. ChrisA