On Mon, May 25, 2020 at 02:03:49AM +0100, Rob Cliffe via Python-ideas wrote:

[quoting Dominik Vilsmeier]
> >It looks like the most common use case for this is to deal with 
> >mutable defaults

It might be the most common use case, but it's hardly the only use case.

The PEP talks about special cases :-) 

I can tell you that it is very frustrating to what to do something and 
not be able to do it, not for good design reasons, or for implementation 
reasons, but simply because the language designers chose to support only 
a subset of behaviour.

Mutable defaults is a subset of late-binding defaults. A solution to 
late-binding automatically solves the mutable default question. Why 
settle for half a solution?

Looking at some of my mutable defaults, I've used the usual `[]` and 
`{}` of course, but I've also used `[0]` amd other pre-populated 
lists, sets and dicts, and instances of custom classes. I've also used 
default values that are functions themselves, which are technically 
mutable even if we don't typically mutate them.

I've also used plenty of *immutable* defaults, where I have wanted them 
recalculated on each use.


[Rob]
> Idea: Invent a new kind of string which behave like f-strings, called 
> say g-strings.

G-strings? *cough*


>           Add a rule that if the default value of an argument is 
> (an expression containing) a g-string,
>           the default value is recalculated every time the function 
> is called and a value for that argument is not passed.

That's pretty much the same rule I've been working on for my proto-PEP, 
except for the "g-string" part.

I know that f-strings have become super-popular, but that doesn't make 
every piece of syntax a nail that we hammer with the "random-letter"- 
string until we have 26 different string prefixes :-)


[...]
> (Possibly heretical) Thought:
> ISTM that when the decision was made that arg default values should be 
> evaluated
>         once, at function definition time,
> rather than
>         every time the function is called and the default needs to be 
> supplied that that was the *wrong* decision.

It really wasn't a mistake. Look at your default values: the great 
majority of defaults need to be evaluated only once. Making every 
default value a late-bound expression would be wasteful of time and 
memory.

(Although a sufficiently smart compiler could minimize that waste in the 
common case of immutable literals. How? By sneakily shifting to early 
binding and avoiding the late evaluation!)

There's also at least two execution models for late binding that I know 
of, and which ever one you chose, some people would complain that it's 
not the right one.

If you can only have one model for function defaults, early binding is 
the clear winner. With early binding, it is easy to implement late 
binding in the function body using the "if None: arg = value" idiom. But 
with late binding, it is *seriously* inconvenient and difficult to go 
the other way and implement early binding semantics.


> But it is a constant surprise to newbies (and sometimes not-so-newbies).

Yes, people say that they want late binding. Then they use closures, 
which implement late binding, and they complain that it's a bug and it 
was the wrong decision to use late binding. Then they go back to writing 
a function definition, and complain that they should have used late 
binding.

I remind you that the usual work-around for the "closures use late 
binding" gotcha is to use *early binding* to fix it.

The gotcha:

    py> def func():
    ...     L = []
    ...     for i in range(3):
    ...         # closure using late binding for i
    ...         L.append(lambda: 100+i)
    ...     return [f() for f in L]
    ...
    py> func()
    [102, 102, 102]


The solution is to work around the problem with early binding:

    py> def func():
    ...     L = []
    ...     for i in range(3):
    ...         # work around the problem
    ...         L.append(lambda i=i: 100+i)
    ...     return [f() for f in L]
    ...
    py> func()
    [100, 101, 102]


If functions used late binding for all defaults:

- people would still be surprised, just as they are with closures;

- it would be wasteful of time and memory, slowing down function 
  calls for no good reason;

- it would make early binding semantics really difficult;

- and it would destroy the work-around for late binding, preventing it 
  from working.

Late binding of function defaults an option? Sure.

But if Python had used late binding for all defaults, with no way to opt 
out, the language would be significantly worse: slower, heavier memory 
usage, and more annoying to use.


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

Reply via email to