On Fri, Jun 24, 2022 at 1:06 AM Chris Angelico <ros...@gmail.com> wrote:

> On Fri, 24 Jun 2022 at 13:26, Joao S. O. Bueno <jsbu...@python.org.br>
> wrote:
> >
> >
> >
> > On Thu, Jun 23, 2022 at 2:53 AM Chris Angelico <ros...@gmail.com> wrote:
> >>
> >> On Thu, 23 Jun 2022 at 11:35, Joao S. O. Bueno <jsbu...@python.org.br>
> wrote:
> >> >
> >> > Martin Di Paola wrote:
> >> > > Three cases: Dask/PySpark, Django's ORM and selectq. All of them
> >> > > implement deferred expressions but all of them "compute" them in
> very
> >> > > specific ways (aka, they plan and execute the computation
> differently).
> >> >
> >> >
> >> > So - I've been hit with the "transparency execution of deferred code"
> dilemma
> >> > before.
> >> >
> >> > What happens is that: Python, at one point will have to "use" an
> object - and that use
> >> > is through calling one of the dunder methods. Up to that time, like,
> just writing the object name
> >> > in a no-operation line, does nothing. (unless the line is in a REPL,
> which will then call the __repr__
> >> > method in the object).
> >>
> >> Why are dunder methods special? Does being passed to some other
> >> function also do nothing? What about a non-dunder attribute?
> >
> >
> > Non-dunder attributes goes through obj.__getattribute__  at which point
> evaluation
> > is triggered anyway.
>
> Hmm, do they actually, or is that only if it's defined? But okay. In
> that case, simply describe it as "accessing any attribute".
>
> >> Especially, does being involved in an 'is' check count as using an
> object?
> >
> >
> > "is" is not "using', and will be always false or true as for any other
> object.
> > Under this approach, the delayed object is a proxy, and remains a proxy,
> > so this would have side-effects in code consuming the object.
> > (extensions expecting strict built-in types might not work with a
> > proxy for an int or str) - but "is" comparison should bring 0 surprises.
>
> At this point, I'm wondering if the proposal's been watered down to
> being nearly useless. You don't get the actual object, it's always a
> proxy, and EVERY attribute lookup on EVERY object has to first check
> to see if it's a special proxy.
>
> >> dflt = fetch_cached_object("default")
> >> mine = later fetch_cached_object(user.keyword)
> >> ...
> >> if mine is dflt: ... # "using" mine? Or not?
> >>
> >> Does it make a difference whether the object has previously been poked
> >> in some other way?
> >
> >
> > In this case, "mine" should be a proxy for the evaluation of the call
> > of "fetch_cached_object" which clearly IS NOT the returned
> > object stored in "dflt".
> >
> > This is so little, or so much, surprising as verifying that "bool([])"
> yields False:
> > it just follows the language inner workings, with not special casing.
>
> If it's defined as a proxy, then yes, that's the case - it will never
> be that object, neither before nor after the undeferral. But that
> means that a "later" expression will never truly become the actual
> object, so you always have to keep that in mind. I foresee a large
> number of style guides decrying the use of identity checks because
> they "won't work" with deferred objects.
>
> > Of course, this if this proposal goes forward - I am just pointing that
> the
> > existing mechanisms in the language can already support it in a way
> > with no modification. If "is" triggering the resolve is desired, or if
> > is desired the delayed object should  be replaced "in place", instead
> > of using a proxy, another approach would be needed - and
> > I'd favor the "already working" proxy approach I presented here.
> >
> > (I won't dare touch the bike-shedding about the syntax on this, though)
> >
>
> Right, but if the existing mechanisms are sufficient, why not just use
> them? We *have* lambda expressions. It wouldn't be THAT hard to define
> a small wrapper - okay, the syntax is a bit clunky, but bear with me:
>
> class later:
>     def __init__(self, func):
>         self.func = func
>         self.__is_real = False
>     def __getattribute__(self, attr):
>         self.__makereal()
>         return getattr(self.__wrapped, attr)
>     def __makereal(self):
>         if self.__is_real: return
>         self.__wrapped =  self.func()
>         self.__is_real = True
>
> x = later(lambda: expensive+expression()*to/calc)
>
> And we don't see a lot of this happening. Why? I don't know for sure,
> but I can guess at a few possible reasons:
>
> 1) It's not part of the standard library, so you have to go fetch a
> thing to do it. If that's significant enough, this is solvable by
> adding it to the stdlib, or even a new builtin.
>
> 2) "later(lambda: expr)" is clunky. Very clunky. Your proposal solves
> that, by making "later expr" do that job, but at the price of creating
> some weird edge cases (for instance, you *cannot* parenthesize the
> expression - this is probably the only place where that's possible, as
> even non-expressions can often be parenthesized, eg import and with
> statements).
>
> 3) It's never actually the result of the expression, but always this proxy.
>
> 4) There's no (clean) way to get at the true object, which means that
> all the penalties are permanent.
>
> 5) Maybe the need just isn't that strong.
>
> How much benefit would this be? You're proposing a syntactic construct
> for something that isn't used all that often, so it needs to be a
> fairly dramatic improvement in the cases where it _is_ used.
>
>
Excuse-me
Who is the "you" you are referring to in the last paragraphs?
(honest question)

I am not proposing this - the proto-pep is David Mertz' .

I just pointed out that the language, as it is today,can handle
the inner part of the deferred object, as it is.
(if one just adds all possible  dunder methods to your proxy example
above, for example)
Moreover, there could be an attribute namespace to deal/modify the object
so - retrieving the "real" object could be trivial. (the original would
actually be retrieved in _any_ operation with with the object that would
make use of its dunder attributes - think "str", or "myobj + 3", since the
proxy
dunder would forward the operation to the wrapped object corresponding
method.

I am talking about this because I had played around with that "transparent
future object"
in the Lelo project I linked in the other e-mail, and it just works, and
actually looks like magic, due to it auto-resolving whenever it is
"consumed".

But like you, I don't know how useful it would actually be - so I am not
the "you"
from your last paragraphs - I'd not used the "lelo" proxy in production
code:
calling a ".result()" method, or, in these days, having an "await"
expression offers
 something with a lot more control

I just wrote because it is something I made work before - and if there are
indeed
uses for it, the language might not even need changes to support it
beyond an operator keyword.

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

Reply via email to