
On Mon, May 24, 2021 at 06:36:47PM -0700, micro codery wrote:
Basically this would add syntax to python that would transform @decorator("spam this") variable into variable = decorator("variable", "spam this")
That is confusingly different from decorator syntax in other contexts.
Your proposal appears to be:
@decorator(expression) targetname
# transformed into:
targetname = decorator("targetname", expression)
But in class and function decorator contexts, the equivalent syntax is equivalent to:
@decorator("spam this") def func(): pass
# transformed to:
def func(): pass func = decorator("spam this")(func)
except that in CPython it is not actually implemented as two separate steps like that.
But the critical difference is that the argument "spam this" should be passed to the decorator *factory*, which then returns the actual decorator that gets applied to the variable. (Or function/ class in the case of regulator decorator syntax.)
To my mind, the only interesting part of this proposal is access to the binding target name. If we remove that from the proposal, variable decorators are nothing more than function calls, and we already can do that:
variable = decorator("spam this")
Wanting the name of the binding target doesn't come up often, but when it does, it's often very useful for the right-hand side of the assignment to know what the left hand side is, as a string, but the only way to do so is to manually provide it:
# A trivial example. myclass = type("myclass", bases, namespace)
# A common example. RED = "RED"
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. Then:
RED = @@ # transforms to `RED = 'RED'`
GREEN = "dark " + @@.lower() # transforms to `GREEN = "dark " + 'GREEN'.lower()`
myclass = type(@@, bases, namespace) # transforms to `myclass = type('myclass', bases, namespace)`
# Not all functions expect the name as first argument. result = function(arg, value, @@) # transforms to `result = function(arg, value, 'result')`
If there's no target, it resolves to None:
print(@@) # print(None)
or if people prefer a SyntaxError, I'm okay with that too.
Targets aren't limited to a single bare name.
spam.eggs = @@ # spam.eggs = 'spam.eggs'
mylist[2] = @@ # mylist[2] = 'mylist[2]'
If the key or index is not known at compile-time, it is a syntax error:
mylist[getindex()] = @@ # SyntaxError
Chained assignments transform to a tuple of target names:
spam = eggs = cheese = func(arg, @@) # spam = eggs = cheese = func(arg, ('spam', 'eggs', 'cheese'))
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'))
This would be good for sympy:
a, b, c, d, w, x, y, z = sympy.symbols(@@)
Target resolution is performed at compile-time, not runtime. There's no global variable called "@@". That means that this won't work:
code = compile("type(@@, bases, namespaces)", '', 'single') # above transforms to `type(None, bases, namespace)` myclass = eval(code)
But I think that restriction is fine.