On Thu, Nov 04, 2021 at 03:07:22AM +1100, Chris Angelico wrote:

> def func(spam: list = []) -> str: ...
> 
> Which part of that becomes late bound?

The name "spam" of course. Without the parameter, nothing gets bound at 
all.


> The [], not the name "list", not the name "spam".

The type annotation is irrelevant. Its an annotation. There's no sense 
that the earliness/lateness of the binding comes into it at all. The 
parameter spam is declared to be a list even if the function is never 
called and nothing gets bound to it at all.

And the [] is merely the expression that is bound to the parameter.

I say "merely", but of course the expression is important in the sense 
that if you want the default value to be an empty list, it won't do to 
use "Hello world" as the expression. Obviously.

But the parameter is, in a sense, more important than the default value, 
because the default value is only a default. If you pass an argument to 
the function, the default doesn't get used. So if you talk about the 
function (either in code, the function's body, or in actual prose), you 
will say something like:

    if condition:
        spam.append(eggs)

"if this condition is true, append eggs to spam" rather than "...to the 
empty list" because it might not be the empty list. We talk about the 
parameter. The parameter is late bound. Not the empty list.

Which empty list? My program might generate millions of them. The only 
one that matters here is spam. It is *spam* which is late bound.

Late binding or early binding is a property of the identifier, not of 
the value that gets bound to it.

Putting the late-bound modifier on the assignment buries it in the 
middle of what might be a fairly complicated (or hideously complicated!) 
signature with a type annotations and an expression for the default 
value:

    parameter:Optional[Literal[X]|List[T]]=>(alpha if value > 2 else 
beta)*gamma ...

I'm sure that we can make it unambiguous to the compiler, but we may not 
make it stand out sufficiently to the human reader. It should be front 
and centre, like the `*` in the `*args` and `**kwargs` syntax.

I personally feel that the @ symbol works as a prefix modifier on the 
parameter. But if you hate the @ symbol, I think that other prefix 
modifiers may also work:

    $parameter=expression  # perhaps a little too like Sparameter
    !parameter=expression  # "bang" parameter :-)
    ^parameter=expression
    >parameter=expression  # unary greater than :-)
    ~parameter=expression

Perl calls these symbols that modify an identifier "sigils". Other 
languages have them too:

https://en.wikipedia.org/wiki/Sigil_(computer_programming)

I guess this is not really a "true" sigil, because the modifier doesn't 
follow the parameter name outside of the function header (likewise for 
the `*args` and `**kwargs` syntax). But the analogy is close: the signal 
instructs the compiler that this identifier needs to be treated 
differently by using late-binding instead of early for the default, just 
as the `*` and `**` sigils instruct the compiler that they are to 
collect extra positional and keyword arguments.


-- 
Steve
_______________________________________________
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/P46SWHU5UTDGXIS6IK74JAFX4N3BQIBP/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to