This is very much implementation dependent. And I do not have a big opinion here as I certainly haven’t spent enough time to have one. I am just considering functionality from user’s perspective for the time being.
Let's tackle just this one part for a moment. What does "ensure_eval" do? Evaluate a proxy object but not evaluate anything else?
In case of the library I used, it would look something like: def ensure_eval(x): if isisnstance(x, Proxy) and x.unevaulated: x.evaluate() return x
This should always succeed, right? Well, what if x is itself a Proxy object? How does it know not to reevaluate it?
Not necessarily. I mean, yes, but only if deferred evaluation is done by actually replacing value in the namespace dictionary. In case of this example, where this specific package is used, it is not so and is not intended to be so. When operation is performed, the value is evaluated and returned, but it remains proxy object. So if lazy is nested, the whole nesting is evaluated on first operation which needs its value. I see this one as “decision to be made” as opposed to “reason why it’s not working”. I am not sure about the “right” approach here. Actually replacing deferred object with a value does sound invasive, although given there was a way to do it elegantly and without loss in performance, it seems to be more robust and eliminates maintenance of proxy object. So the big question is, which of the 2 approaches to take: 1) Implementing a robust proxy object. Less depth more breadth - ensuring proxy works with all objects / coming up with protocols, which have to be implemented for non-standard types to be compatible. In this case both of assertions will and should fail, although `assert y is y_` will always pass. Example from library: obj = Proxy(lambda: 1) obj2 = Proxy(lambda: obj) obj is obj2 # False print(obj == obj2) # True 2) Replacing deferred evaluation with it’s value. in this case, a fair bit of low level decisions such as the one you indicated would have to be made, a lot of deep strings to pull In this case, it very much depends how “is”, “type” and other `things` behave with deferred objects. So if `x` is a proxy, and ‘is’ treats proxy object and not it’s value (which is reasonable), then assertions will and should fail. However, unraveling the whole stack on first evaluation does simplify things a bit.Then ` y_ = ensure_eval(y)` in your example is a redundant line. In practice, if deferred evaluation is used, one would need to: `assert ensure_eval(x) is ensure_eval(y)`. Also, looking at deferred eval RST doc, the suggested mechanics are that expression is evaluated if “later” is not re-called. So in theory, it might be reasonable to have `x = later expr() <equiv> x = later later later expr()`. Instead of evaluating the whole stack, just not allowing it to grow in the first place. Then builtin `ensure_eval` is then straight forward single operation at C level, which would mostly be used on low level checks, such as asserts in your examples. It could even have its syntax instead of builtin function, given there are only 2 entry points (definition of deferred & ensuring it’s evaluation). E.g. def expr(): print('Evaluating') return 1 a = `expr()` b = `a` # carry over lazy, but it's a new object at C level assert a is b # False assert !a is !b # True assert a is b # True
On 21 Jul 2023, at 08:46, Chris Angelico <rosuav@gmail.com> wrote:
On Fri, 21 Jul 2023 at 11:08, Dom Grigonis <dom.grigonis@gmail.com> wrote:
Also, can't find a way to ONLY force evaluation without any additional operations in this specific library. A simple callable which evaluates if unevaluated & returns would do. Then:
def IF(condition, when_true, when_false): if condition: return ensure_eval(when_true) else: return ensure_eval(when_false)
Controls evaluation if deferred objects are provided, but can also be used with `normal` values.
Let's tackle just this one part for a moment. What does "ensure_eval" do? Evaluate a proxy object but not evaluate anything else? That seems simple, but might very well be straight-up wrong. Consider:
def test_proxy(x): x_ = Proxy(x) y = ensure_eval(x_) y_ = ensure_eval(y) assert x is y assert x is y_
This should always succeed, right? Well, what if x is itself a Proxy object? How does it know not to reevaluate it?
ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@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/GTCRUP... Code of Conduct: http://python.org/psf/codeofconduct/