On Apr 17, 2020, at 23:18, Steven D'Aprano <st...@pearwood.info> wrote:
> 
> 
> Keyword Unpacking Shortcut
> --------------------------
> 
> Inside function calls, the syntax
> 
>  **{identifier [, ...]}
> 
> expands to a set of `identifier=identifier` argument bindings.
> 
> This will be legal anywhere inside a function call that keyword 
> unpacking would be legal.

Which means that you can’t just learn ** unpacking as a single consistent thing 
that’s usable in multiple contexts with (almost) identical syntax and identical 
meaning, you have to learn that it has an additional syntax with a different 
meaning in just one specific context, calls, that’s not legal in the others. 
Each special case like that makes the language’s syntax a little harder to 
internalize, and it’s a good thing that Python has a lot fewer such special 
cases than, say, C.

Worse, this exact same syntax is a set display anywhere except in a ** in a 
call. Not only is that another special case to learn about the differences 
between set and dict displays, it also means that if you naively copy and paste 
a subexpression from a call into somewhere else (say, to print the value of 
that dict), you don’t get what you wanted, or a syntax error, or even a runtime 
error, you get a perfectly valid but very different value.

> On the other hand, plain keyword unpacking:
> 
>  **textinfo
> 
> is terse, but perhaps too terse. Neither the keys nor the values are 
> immediately visible. Instead, one must search the rest of the function 
> or module for the definition of `textinfo` to learn which parameters are 
> being filled in.

You can easily put the dict right before the call, and when you don’t, it’s 
usually because there was a good reason.

And there are good reasons. Ideally you shouldn’t have any function calls that 
are so hairy that you want to refractor them, but the the existence of 
libraries you can’t control that are too huge and unwieldy is the entire 
rationale here. Sometimes it’s worth pulling out a group of related parameters 
to a “launch_params” or “timeout_and_retry_params” dict, or even to a 
“build_launch_params” method, not just for readability but sometimes for 
flexibility (e.g., to use it as a cache or stats key, or to give you somewhere 
to hook easily in the debugger and swap out the launch_params dict.

> Backwards compatibility
> -----------------------
> 
> The syntax is not currently legal so there are no backwards 
> compatibility concerns.

The syntax is perfectly legal today. The syntax for ** unpacking in a call 
expression takes any legal expression, and a set display is a legal expression. 
You can see this by calling compile (or, better, dis.dis) on the string 
'spam(**{a, b, c})'.

The semantics will be a guaranteed TypeError at runtime unless you’ve done 
something pathological, so almost surely nobody’s deployed any code that 
depends on the existing semantics. 

But that’s not the same as the syntax not being legal. And, outside of that 
trivial backward compatibility nit, this raises a bunch of more serious issues. 

Running Python 3.9 code in 3.8 would do the wrong thing, but maybe not wrong 
enough to break your program visibly, which could lead to some fun debugging 
sessions. That’s not a dealbreaker, but it’s definitely better for new syntax 
to raise a syntax error in old versions, if possible.

And of course existing linters, IDEs, etc. will misunderstand the new syntax 
(which is worse than failing to parse it) until they’re taught the new special 
case.

This also raises an implementation issue. The grammar rule to disambiguate this 
will probably either be pretty hairy, or require building a parallel fork of 
half the expression tree so you can have an “expression except for set 
displays” node. Or there won’t be one, and it’ll be done as a special case 
post-parse hack, which Python uses sparingly.

But all of that goes right along with the human confusion. If the same syntax 
can mean two different things in different contexts, it’s harder to internalize 
a usable approximate version of the grammar. For something important enough, 
that may be worth it, but I don’t think the benefits of this proposal reach 
that bar.

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

Reply via email to