On Thu, Dec 9, 2021 at 5:52 PM Brendan Barnwell <brenb...@brenbarn.net> wrote:
>
> On 2021-12-08 20:55, Chris Angelico wrote:
> > (H) This is true. But if the two syntaxes can be sufficiently similar,
> > the cost should be low, and the feature benefit would be high. Early
> > binding lets you "def f(x=x):" in a loop and capture each x as it goes
> > by. Late binding lets you "def f(x=>[]):" and get a new list every
> > time. Both have their places.
>
>         (The "two syntaxes" here is referring to syntaxes for early and late
> binding.)  I'm actually worried about the reverse.  When the two
> syntaxes are similar, it will be easier to mistake one for the other.

For the most part, there won't be any confusion. If the default is a
simple value, it's going to be early-bound (eg "def
list.pop(index=-1):"). If it's a mutable that needs to be constructed,
it'll be late-bound. The distinction will be important if it's looked
up from another namespace ("def
fetch(timeout=some.default.timeout):"), but even then, the distinction
is whether the default is affected by future changes or not; the two
forms are functionally very similar.

> > x = 1/y if y else "invalid"
> >
> > There's no object for "1/y". Trying to create one would be a nightmare
> > of subtleties, where assignment expressions would break things,
> > nonlocal variable references would become tricky, etc, etc. Similarly:
>
>         As I have stated repeatedly, ternary expressions are not parallel
> because they do not have a distinct definition-time and call-time.
> Please stop bringing up the ternary operator case and pretending it is
> the same as a function.  It is not, for reasons that I have already
> explained several times.
>
>         To try stating this in yet another way, currently if I have:
>
> def f(a=<some code here>)
>
>         <some code here> must be something that evaluates to a first-class
> object, and the "argument default" IS that first-class object --- not
> bytecode to generate it, not some behavior that evaluates the
> expression, no, the default value is itself an object.  This would not
> be the case for late-bound defaults under the PEP.  (Rather, as I
> phrased it in another post, there would not "be" a late-bound default at
> all; there would just be some behavior in the function to do some stuff
> when that argument isn't passed.)

The VALUE is a first-class object - that's the result of evaluating
the expression. With early-bound defaults, that's the only thing that
gets saved - not the expression, just the resulting value. (Which can
be seen if you do something like "def f(x=0x100):", which will show
the default as 256.)

Remember, a late-bound default is most similar to this code:

def f(a=<optional>):
    if a was omitted: a = <some code here>

And in that form, the code isn't available as a first-class object.
That's why I say that it is parallel to every other partial expression
in Python. Until you evaluate it, there is no first-class object
representing it. (A code object comes close, but it's more than just
an expression - it also depends on its context. A function requires
even more context.)

Suppose you wanted, for a function with an early-bound default, to see
the actual expression that got you there. This can be synthesized in
some cases (eg an enum with a good repr), but in the general case, how
can you figure out that expression? Could you reevaluate it? Suppose
you wanted to make a decorator like this:

@reevaluate_defaults_every_call
def f(x=[], y=func(), z=x.length + y): ...

Could you, using early-bound defaults, get hold of the expressions?
Are they first-class objects?

ChrisA
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/GVOGD5VUVZF24KUAC5UHQGAVFNVZSSIV/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to