I proposed a very toy example of a coalescing class that would solve the
problem solved by brand new syntax in PEP 505.  In my 5 minute version,
there were several faults or limitations.  For one, the class I wrote was
misnamed, since it wasn't really about None-coalescing, but rather about
exception-free nested access.

I've written a more robust version at https://pypi.org/project/coalescing/.
I encourage issues or contributions at
https://github.com/DavidMertz/coalesce.  Mind you, this is still a
few-hour-of-effort version, not rigorously tested.

I want to thank Antoine Pitrou for proposing using Graham Dumpleton's
warpt.ObjectProxy (which is truly magic).  In almost no lines, I think I
have something perhaps even better than PyMaybe.  In particular, most of
the time, there is no need to do an explicit `.unbox()` call... but at the
suggestion of several people, I changed my first API to optionally pass a
default value other than None within that method, if desired.  I even made
laziness in getting that value the default (but switchable) behavior.

Dumpleton's magic means that boxed values can do pretty much anything the
raw values can, so can generally ignore the .unbox() call at the end.
Perhaps most magic of all, you can even ASSIGN into proxied values and that
gets written to the original value (not shown in the doctests below).  This
adds something powerful that PEP 505 is completely unable to do.

I believe that I have captured ALL the semantics of PEP 505 with no changes
to Python syntax... and in a way that reads FAR better than all those new
operators do.

Access messy nested data structures

GreedyAccess will keep marching down trees even if failure occurred earlier:

    >>> from coalesce import GreedyAccess, make_test
    >>> cfg = make_test()
    >>> GreedyAccess(cfg).user.profile.song
    <GreedyAccess proxy for 'Nightclubbing'>
    >>> GreedyAccess(cfg).user.profile.song + ' and spam'
    'Nightclubbing and spam'
    >>> GreedyAccess(cfg).user.profile.food
    <GreedyAccess proxy for None>
    >>> print(GreedyAccess(cfg).user.profile.food.unbox())
    None
    >>> GreedyAccess(cfg).user.profile.food.unbox('spam')
    'spam'
    >>> GreedyAccess(cfg).user.profile.food.unbox('spam') + ' and spam'
    'spam and spam'

NoneCoalesce only descends until a None is encountered.  Accessing
attributes
or keys of None will still fail:

    >>> from coalesce import NoneCoalesce
    >>> NoneCoalesce(cfg).user.profile.song
    <NoneCoalesce proxy for 'Nightclubbing'>
    >>> NoneCoalesce(cfg).user.profile.song.unbox()
    'Nightclubbing'
    >>> NoneCoalesce(cfg).user.profile.food
    Traceback (most recent call last):
        ...
    AttributeError: 'types.SimpleNamespace' object has no attribute 'food'
    >>> NoneCoalesce(cfg).user.profile
    <NoneCoalesce proxy for namespace(arms=2, song='Nightclubbing')>

    >>> val = None
    >>> print(NoneCoalesce(val).attr)
    None

We provide for returning values other than None if some other default is
more
useful for your use case (a zero-argument lambda function would often be
useful here):

    >>> def say_spam():
    ...     return "spam"
    ...
    >>> GreedyAccess(cfg).user.profile.food.unbox(say_spam)
    'spam'
    >>> GreedyAccess(cfg).user.profile.food.unbox(say_spam, lazy=False)
#doctest: +ELLIPSIS
    <function say_spam ...>


-- 
Keeping medicines from the bloodstreams of the sick; food
from the bellies of the hungry; books from the hands of the
uneducated; technology from the underdeveloped; and putting
advocates of freedom in prisons.  Intellectual property is
to the 21st century what the slave trade was to the 16th.
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to