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 -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/P46SWHU5UTDGXIS6IK74JAFX4N3BQIBP/
Code of Conduct: http://python.org/psf/codeofconduct/