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.

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

Reply via email to