On Tue, May 25, 2021 at 8:11 AM Chris Angelico <rosuav@gmail.com> wrote:
On Tue, May 25, 2021 at 5:29 PM Steven D'Aprano <steve@pearwood.info> wrote:
> Here's a counter-proposal: we have a special symbol which is transformed
> at compile-time to the left hand assignment target as a string. Let's
> say we make that special expression `@@` or the googly-eyes symbol.

This is sounding promising. I'm liking this.


> Chained assignments transform to a tuple of target names:
>    spam = eggs = cheese = func(arg, @@)
>    # spam = eggs = cheese = func(arg, ('spam', 'eggs', 'cheese'))

Hmm. Everything else gives you a single string, this one doesn't. I'd
actually be inclined to switch around this one and the next one...

> Sequence unpacking assignment gets transformed as a single
> comma-seperated string:
>     spam.eggs, foo, *bar = func(arg, @@)
>     # spam.eggs, foo, *bar = func(arg, ('spam.eggs,foo,*bar'))

... so that assigning the same thing to multiple names gives you a
space-separated string (or equals-separated, "spam=eggs=cheese"), but
unpacking gives you a tuple of targets, since it then nicely parallels
the result it's expecting from the function. That would mean that:

# This assigns a single string to them all eg "spam eggs cheese"
spam = eggs = cheese = @@
# This assigns a string to each one:
spam, eggs, cheese = @@
# and is equivalent to:
spam = @@; eggs = @@; cheese = @@


No wrong answers. (Well, unless you say "tomato". That is a very wrong
answer to a yes/no question.)

I'm liking this. It might mean that class syntax and decorator abuse
become less necessary as ways to get around name duplication.


(In the spirit of no wrong answers!)

One of the motivating examples from the OP's post was symbolic math. As such I think this looks pretty nice (side note: in sympy the sympy.Symbol factory name is symbols, not symbol):

#  x = symbols("x")
@symbols x

For a single symbol, it would be this using Steven D's googly-eyes counter proposal, which also looks good to me:

#  x = symbols("x")
x = symbols(@@)

I think Steven makes a good case that the googly-eyes brings with it the best part of the OP's proposal, and avoids inconsistent, probably confusing decorator behavior (compared to function decorators).

Continuing on with Steven's counter proposal and the motivating example of symbolic math, for multiple symbols it would be this:

#  x, y, z = symbols( 'x,y,z' )
x, y, z = symbols(@@) 

However, even though it works for the symbols example, this meaning looks odd to me:

#  x, y, z = symbols( 'x,y,z' )

...it seems like this should in fact be three separate assignment operations, not a single assignment operation. So, instead:

x, y, z = symbols(@@) 
# three assignments, not one (and this is Chris A's suggestion!):
#  x = symbols( 'x' );  y = symbols( 'y' );  z = symbols( 'z' )

This version also works for the symbols example, though it calls the factory function 3 times rather than 1. Is that an important downside?

I am on the fence for the better meaning of this:

x = y = z = symbols(@@) 
#  x = y = z = symbols( ('x', 'y', 'z') )  # Steven's -- produces (Symbol('x'), Symbol('y'), Symbol('z')) )
#  x = y = z = symbols( 'x y z' )  # Chris' version 1  -- produces (Symbol('x'), Symbol('y'), Symbol('z')) 
#  x = y = z = symbols( 'x=y=z' )  # Chris' version 2  --  produces Symbol('x=y=z')

As noted above, for all three versions if a user tried this it may appear to work at first (no immediate errors) but in actuality, x, y, and z would almost certainly not contain what the user wants. This seems potentially frustrating?

I note that the symbols example is not the be-all end-all example, but going through it was instructive for me on what the differences of Steven's and Chris' suggestions are. And maybe other examples would behave similarly? Unsure.


"I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler