[Python-ideas] A "local" pseudo-function

Steve Barnes gadgetsteve at live.co.uk
Sat Apr 28 22:51:05 EDT 2018



On 28/04/2018 04:34, Yury Selivanov wrote:
> Hi Tim,
> 
> This is interesting. Even "as is" I prefer this to PEP 572. Below are some
> comments and a slightly different idea inspired by yours (sorry!)
> 
> On Fri, Apr 27, 2018 at 10:41 PM Tim Peters <tim.peters at gmail.com> wrote:
> [..]
>> As an expression, it's
> 
>>       "local" "(" arguments ")"
> 
>> - Because it "looks like" a function call, nobody will expect the targets
>>     of named arguments to be fancier than plain names.
> [..]
>> Everyone's favorite:
> 
>> if local(m = re.match(regexp, line)):
>>       print(m.group(0))
> 
>> Here's where it's truly essential that the compiler know everything
>> about "local", because in _that_ context it's required that the new
>> scope extend through the end of the entire block construct (exactly
> 
> It does look like a function call, although it has a slightly different
> syntax. In regular calls we don't allow positional arguments to go after
> keyword arguments.  Hence the compiler/parser will have to know what
> 'local(..)' is *regardless* of where it appears.
> 
> If you don't want to make 'local' a new keyword, we would need to make the
> compiler/parser to trace the "local()" name to check if it was imported or
> is otherwise "local". This would add some extra complexity to already
> complex code.  Another problematic case is when one has a big file and
> someone adds their own "def local()" function to it at some point, which
> would break things.
> 
> Therefore, "local" should probably be a keyword. Perhaps added to Python
> with a corresponding "from __future__" import.
> 
> The other way would be to depart from the function call syntax by dropping
> the parens.  (And maybe rename "local" to "let" ;))  In this case, the
> syntax will become less like a function call but still distinct enough.  We
> will be able to unambiguously parse & compile it.  The cherry on top is
> that we can make it work even without a "__future__" import!
> 
> When we implemented PEP 492 in Python 3.5 we did a little trick in
> tokenizer to treat "async def" in a special way. Tokenizer would switch to
> an "async" mode and yield ASYNC and AWAIT tokens instead of NAME tokens.
> This resulted in async/await syntax available without a __future__ import,
> while having full backwards compatibility.
> 
> We can do a similar trick for "local" / "let" syntax, allowing the
> following:
> 
>     "let" NAME "=" expr ("," NAME = expr)* ["," expr]
> 
> * "if local(m = re.match(...), m):" becomes
>      "if let m = re.match(...), m:"
> 
> * "c = local(a=3) * local(b=4)" becomes
>     "c = let a=3, b=4, a*b" or "c = (let a=3, b=4, a*b)"
> 
> *      for i in iterable:
>             if let i2=i*i, i2 % 18 == 0:
>                append i2 to the output list
> 
> etc.
> 
> Note that I don't propose this new "let" or "local" to return their last
> assignment. That should be done explicitly (as in your "local(..)" idea):
>    `let a = 'spam', a`.  Potentially we could reuse our function return
> annotation syntax, changing the last example to `let a = "spam" -> a` but I
> think it makes the whole thing to look unnecessarily complex.
> 
> One obvious downside is that "=" would have a different precedence compared
> to a regular assignment statement. But it already has a different precedent
> in function calls, so maybe this isn't a big deal, considered that we'll
> have a keyword before it.
> 
> I think that "let" was discussed a couple of times recently, but it's
> really hard to find a definitive reason of why it was rejected (or was it?)
> in the ocean of emails about assignment expressions.
> 
> Yury
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> https://mail.python.org/mailman/listinfo/python-ideas
> Code of Conduct: http://python.org/psf/codeofconduct/
> 
If things were to go the keyword direction I personally think that a 
great deal of clarity could be achieved by borrowing somewhat from 
Pascal with either

   use <exp> as <local_name>[, <exp> as  <local_name> ...]:
       # Local names only exist in this scope,
       # existing names are in scope unless overridden
       # new names introduced here go out to nesting scope

OR (for those who prefer name first, a function like look, and slightly 
less typing):

   using(<local_name>=<exp>[ ,<local_name>=<exp>...]):
       # Local names only exist in this scope,
       # existing names are in scope unless overridden
       # new names introduced here go out to nesting scope

Lastly how about:

    <name> = <calculation> using(<local_name>=<exp>[ 
,<local_name>=<exp>...])  # in this case where might be better than using

Presumably, in the latter cases, the using function would return a 
sub_local dictionary that would only exist to the end of the scope be 
that the current line or indented block which should minimise the 
required magic.

-- 
Steve (Gadget) Barnes
Any opinions in this message are my personal opinions and do not reflect 
those of my employer.

---
This email has been checked for viruses by AVG.
http://www.avg.com



More information about the Python-ideas mailing list