[Python-ideas] several different needs [Explicit variable capture list]

Jim J. Jewett jimjjewett at gmail.com
Wed Jan 27 12:27:55 EST 2016


TLDR:
An "extra" defaulted parameter is used for many slightly different
reasons ... even a perfect solution for one of them risks being an
attractive nuisance for the others.


On Tue, Jan 26, 2016 at 11:39 PM, Andrew Barnert <abarnert at yahoo.com> wrote:
> On Jan 26, 2016, at 17:23, Jim J. Jewett <jimjjewett at gmail.com> wrote:

>>> On Tue, Jan 26, 2016 at 3:59 PM, Andrew Barnert <abarnert at yahoo.com> wrote:
>>> On Jan 26, 2016, at 11:40, Jim J. Jewett <jimjjewett at gmail.com> wrote:

>>>> (1)  Auxiliary variables

>>>>   def f(x, _len=len): ...
...
>> It can improve readability, usually by providing a useful rename.

> OK, but then how could FAT, or any optimizer, help with that?

It can't ... and that does argue for aux variables (or a let),
but ... would good usage be swamped by abuse?

You also brought up the case of augmenting a builtin or global,
but still delegating to the original ... I forgot that case, and didn't
even notice that you were rebinding the captured name.  In
those cases, the mechanical intent is "capture the old way",
but the higher level intent is to specialize it.  This should
probably look more like inheritance (or multimethods or
advice and dispatch) ... so even if it deserves a language
change, capture-current-value idioms wouldn't really be an improvement
over the current workaround.

...

>>>> So again, I think something like Victor's FAT optimizer (plus comments
>>>> when immutability really is important) ...

>>> How could an optimizer enforce immutability, much less signal it?

>> Victor's guards can "enforce" immutability by recognizing when it
>> fails in practice.

> But that doesn't do _anything_ semantically--the code runs
> exactly the same way as if FAT hadn't done anything,
> except maybe a bit slower. If that's wrong, it's still just as
> wrong, and you still have no way of noticing that it's wrong,
> much less fixing it. So FAT is completely irrelevant here.

Using the specific guards he proposes, yes.
But something like FAT could provide more active guards
that raise an exception, or swap the original value back
into place, or even actively prevent the modification.

Whether these should be triggered by a declaration in
front of the name, or by a module-level freeze statement,
or ... there are enough possibilities that I don't think a
specific solution should be enshrined in the language yet.

>>  It can't signal, but comments can ... and
>> immutability being semantically important (as opposed to merely useful
>> for optimization) is rare enough that I think a comment is more likely
>> to be accurate than a type declaration.

> Here I disagree completely. Why do we have tuple,
> or frozenset? Why do dicts only take immutable keys?
> Why does the language make it easier to build
> mapped/filtered copies in place? Why can immutable
> objects be shared between threads or processes trivially,
> while mutable objects need locks for threads and heavy
> "manager" objects for processes? Mutability is a very big deal.

Those are all "if you're living with these restrictions anyhow,
and you tell the compiler, the program can run faster."

None of those sound important in terms of "What does this program
(eventually) do?"

(Obviously, when immutability actually *is* important, and an
appropriate immutable data type exists, then *not* using it would send
a bad signal.)

>>> You mean to open a new scope _outside_ the function
>>> definition, so it can capture the cache in a closure, without
>>> leaving it accessible from outside the scope? But then f won't
>>> be accessible either, unless you have some way to "return"
>>> the value to the parent scope. And a scope that returns
>>> something--that's just a function, isn't it?

>> It is a function plus a function call, rather than just a function.
>> Getting that name (possible several names) bound properly in the outer
>> scope is also beyond the abilities of a call.

> It isn't at all beyond the abilities of defining and calling a function. Here's how you solve this kind of problem in JavaScript:
>
>     var spam = function(n) {
>         var cache = {}:
>         return function(n) {
>             if (cache[n] === undefined) {
>                 cache[n] = slow_computation(n);
>             }
>             return cache[n];
>         };
>     }();

That still doesn't bind n1, n2, n3 in the enclosing scope -- it only
binds spam, from which you can reach spam(n1), spam(n2), etc.

I guess I'm (occasionally) looking for something more like

    class _Scope:
        ...
    for attr in dir(_Scope):
        if not attr.startswith("_"):
            locals()[attr] = _Scope[attr]

-jJ


More information about the Python-ideas mailing list