On Nov 30, 2019, at 08:02, Ricky Teachey <ri...@teachey.org> wrote:
> 
> 
> It sounds like many would agree that renaming some things could have many 
> benefits, but the cost seems to be higher than I imagined.
> 
> What if an easier way could be created to manage renaming that avoided many 
> of these problems? 
> 
> Perhaps aliases of objects could be something worth baking into the language 
> itself. I don't mean having multiple names pointing to the same object as we 
> can do today, but something new entirely.

Are you suggesting we allow this so users/projects can add their own aliases 
into the stdlib, or so that the stdlib can grow a whole mess of aliases?

> THE FOLLOWING IS PROBABLY A CRACKPOT IDEA, hear it out of you are in the mood 
> to humor me on this Saturday morning.
> 
> What if the __dict__ for modules and classes could be something like a 
> ChainMap, with multiple internal lookup mappings. The first level (by which I 
> mean, the first mapping consulted) would be the same as exists today, but the 
> second would contain a weak value dictionary with aliases pointing to objects 
> so that when you do json.load_str, it returns json.loads object just like it 
> would if you had added the alias in the way you would today. But the dict is 
> weak value so that if an object is garbage collected, its alias entries are 
> removed.  

So if you monkeypatch json.loads, its last reference might go away, in which 
case json.load_str becomes a dead weakref and can no longer be used? What’s the 
benefit of that? Wouldn’t it be better to make load_str alias loads by name, so 
if an existing module monkeypatches loads, it automatically changes load_str as 
well?

> So the second mapping would be a so called "alias dictionary".
> 
> How could this be done to overcome some of the same objections raised?
> 
> First, regarding discoverability: 
> 
> dir() would work identically to as it does today, but you could give an 
> argument to also provide aliases, something like:
> 
> >>> dir(json, alias=True)
> ['JSONDecodeError', 'JSONDecoder', 'JSONEncoder', '__all__', '__author__', 
> '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', 
> '__name__', '__package__', '__path__', '__spec__', '__version__', 
> '_default_decoder', '_default_encoder', 'codecs', 'decoder', 
> 'detect_encoding', 'dump', 'dumps [dump_str]', 'encoder', 'load', 'loads 
> [load_str]', 'scanner']
> 
> Note that when you tell dir() that alias=True, additional aliases for objects 
> are added to the output in the form of a string-ified list.

But not the same as actually stringifying a list, which gives you the reprs of 
its members.

Also, this means tools now have to understand how to parse this language; you 
can’t just do `for name in dir(spam): something(name)`, you have to do 
something like:

    for name in dir(spam, alias=True):
        if match := re.match(r'(\w+) \[(.*?)\])', name):
            something(match.groups(1))
            for alias in groups(2).split(', '):
                something(alias)
        else:
            something(name)

Of course you could write a helper function to do the heavy lifting, but it’s 
still going to be nontrivial.

And all existing tools that rely on dir (e.g., to autocomplete in a REPL or 
IDE, or to build a proxy object that exposes the same names) aren’t going to 
see aliases until they’re changed to do this.

> Also change the default object repr so that it says something like:
> 
> >>> json.loads
> <function loads (alias: load_str) at 0x.... >

How does it even know that? Does it have to look at its module and name, then 
look at the module’s alias dict?

> Most importantly, it seems like it would be preferred for * importing 
> everything from a module to import only the first level dictionary and not 
> all the aliases.

Doesn’t that defeat the purpose of aliases? If you don’t have them at the REPL 
when you * import, you’re going to learn the “unintuitive” name that is 
available, instead of learning the “intuitive” one.

Also, won’t it be at least annoying, if not confusing, if the help, dir, and 
repr mention the alias but you can’t use it?

> Additionally, you can still add or monkeypatch new objects into a namespace 
> (they would go into the first level dictionary, therefore overriding any 
> aliases in the second level). Beyond that, since I'm talking about something 
> new baked into the language, the code writing tools would be updated to know 
> about aliases and provide useful auto complete functionality that makes it 
> clear this is an alias.

How are you imagining that working? When you autocomplete in the standard REPL 
or ipython or bpython or PyCharm or PyDev, what will they do to indicate that 
it’s an alias?

> For convenient addition of aliases and discovering them for a specific 
> member:  
> 
> There are likely all sorts of ways you could do this. Perhaps a dunder alias 
> combined with an alias decorator could be added (probably in functools? not 
> top level)?
> 
> You could add an alias like:
> 
> @functools.alias("load_str")
> def loads(...): ...
> 
> assert loads.__alias__ == ["load_str"]

Is this dunder a @property that looks at the module’s alias namespace? Or is it 
actually a copy of the same information? If so, how are they kept in sync? (Or 
is that just a “consenting adults” thing—if you only add aliases with the 
decorator the two are in sync, but if you don’t, you have to do it right 
manually or you’ll confuse people.)

> class MyClass:
>     @functools.alias("prof")
>     def process_file(self, ...): ...
> 
> assert MyClass.process_file.__alias__ == ["prof"]

So classes also have an alias dict? And presumably their dir also changes?

And unbound methods obviously have aliases (because they’re just functions), 
but what about bound methods (and classmethods and custom method-y decorators)? 
For example, if `mine = MyClass()`, does `mine.process_file.__alias__` also 
work? How? Do method objects have a @property for the dunder that delegates to 
the underlying `__func__`? If not, I don’t think this would be very useful.

You’d also need to rewrite what __getatrribute__ does to look at aliases on the 
class and base classes (and on the object, or is that not allowed?), which 
would break most existing custom __getattribute__ methods.

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

Reply via email to