On 2015-09-19 02:10, Andrew Barnert via Python-ideas wrote:
On Sep 18, 2015, at 18:00, Chris Angelico
wrote: On Sat, Sep 19, 2015 at 10:49 AM, Andrew Barnert
wrote: Obviously "spam?" returns something with a __getattr__ method that just passes through to spam.__getattr__, except that on NoneType it returns something with a __getattr__ that always returns None. That solves the eggs case. Next, "spam?.cheese?" returns something with a __call__ method that just passed through to spam?.cheese.__call__, except that on NoneType it returns something with a __call__ that always returns None. That solves the cheese case.
Hang on, how do you do this? How does the operator know the difference between "spam?", which for None has to have __getattr__ return None, and "spam?.cheese?" that returns (lambda: None)?
spam None spam? NoneQuestion spam?.cheese None spam?.cheese? NoneQuestion spam?.cheese?() None
All you need to make this work is:
* "spam?" returns NoneQuestion if spam is None else spam * NoneQuestion.__getattr__(self, *args, **kw) returns None. * NoneQuestion.__call__(self, *args, **kw) returns None.
Optionally, you can add more None-returning methods to NoneQuestion. Also, whether NoneQuestion is a singleton, has an accessible name, etc. are all bikesheddable.
I think it's obvious what happens is "spam" is not None and "spam.cheese" is, or of both are None, but if not, I can work them through as well.
I see it as "spam? doing "Maybe(spam)" and then attribute access checking returning None if the wrapped object is None and getting the attribute from it if not. I think that the optimiser could probably avoid the use of Maybe in cases like "spam?.cheese".