On Sat, Oct 16, 2021 at 8:46 AM Steven D'Aprano <steve@pearwood.info> wrote:
On Sat, Oct 16, 2021 at 09:19:26AM -0400, Erik Demaine wrote:

> To me (a mathematician), the existence of this magic in def, class, import,
> etc. is a sign that this is indeed useful functionality.  As a fan of
> first-class language features, it definitely makes me wonder whether it
> could be generalized.

Obviously it is useful, because we have four major uses for it: imports,
classes, functions and decorators. And I can think of at least two minor
uses: the three argument form of type(), and namedtuple.

The question is, are any additional uses worth the effort and potential
ugliness of generalising it?

Not every generalisation is worth it. Sometimes you can overgeneralise.

https://www.joelonsoftware.com/2001/04/21/dont-let-architecture-astronauts-scare-you/

Until we have some compelling use-cases beyond the Big Four, I think
this is a case of YAGNI.

https://www.martinfowler.com/bliki/Yagni.html

I've been thinking about use-cases for this feature for literally
*years*, I've even got a handful of rough notes for a proto-PEP written
down. To my embarrassment, today was the first time I realised that
imports are also an example of this feature. So it is possible that
there are many great use-cases for this and I just can't see them.

You made me realize what the four built-in methods of accessing target
name have in common: they are all building namespaces. Okay so
maybe decorators aren't creating a new namespace, not beyond what
def already does, and def isn't often thought of as a means to create
a new namespace but it certainly does. So not having this access
impeeds a pythonista's ability to create novel namespaces.

This token alone isn't going to make new namespace types effortless,
but it is maybe the last piece not already available at runtime. And yes
we already have SimpleNamespace for just creating a bag of things. But
a SimpleNamespace is still a class object when broken down, It's just
one that does some namespace things better than object.

There is actually at least one more first class use of target names: that
of async def. This is different than def because it is creating a
CoroutineType not simply a FunctionType. Python actually has many
different types that can be created using the def keyword, but async
shows that sometimes it is not even enough to use a decorator, or a
metaclass with a __call__. Having access to the target would allow for
new function types as well as new namespace types.
 
Finally, sometimes the name a class instance is assigned to *is* very
significant and needs to be taken into account when creating it. These
are for instance the classes given here as examples from the typing
module, or for a sentinel class that in only ever be used as instances.
It is not always possible, or desirable, to subclass and work with class
objects rather than instances just to have access to the binding name.

> But I'm not sure what the best mechanism is.

The mechanism is probably easy, if built into the compiler. When the
compiler sees something that looks like a binding operation:

    target = expression

it knows what the target is. If the expression contains some magic
token, the compiler can substitute the target as a string for the magic
token. (There is at least one other possible solution.)

So the implementation is probably easy. It is the design that is hard,
and the justification lacking.

 Yes, this is what my toy PoC does. It replaces the node with a string
while building the AST.

Regards,
~Jeremiah