On Mon, 6 Dec 2021 at 09:45, Stephen J. Turnbull <stephenjturnbull@gmail.com> wrote:
Rob Cliffe via Python-ideas writes:
Nobody has attempted (or at least completed) a PEP, never mind an implementation, of a "generalized deferred object/type", in the last N years or decades.
Haskell anything. Ruby blocks. Closer to home, properties and closures.
So I don't think the object part is that hard, it's the syntax and semantics that's devilish.
At one level, it's trivial. A deferred expression is `lambda: expression`. Evaluating it is `deferred_expr()`. What's not at all obvious is the requirements beyond that - what do people *actually* want that isn't covered by this. The most obvious answer is that they don't want to have to check for a deferred expression and explicitly evaluate it, which triggers the question, when do they want the language to evaluate it for them? "Every time" doesn't work, because then you can't treat deferred expressions as first class objects - they keep disappearing on you ;-) So IMO it's the *requirements* that are hard. Maybe that's just me using different words for the same thing you were saying, but to me, the distinction is important. People throw around the term "deferred object", but everyone seems to think that everyone else understands what they mean by that term, and yet no-one will give a precise definition. We can't have a PEP or an implementation until we know what we're proposing/implementing. I don't intend to champion a "deferred objects" proposal, but I do think that they (whatever they are) would be a better (more general) solution than late-bound arguments. So here's a possible minimal definition of what a "deferred object" is. It takes the view that explicitly requesting the evaluation of a deferred is OK, but people don't want to have to check it's a deferred before evaluating. 1. `defer EXPR` creates a "deferred object", that is semantically identical to `lambda: EXPR`, except that it isn't a callable, instead it's a new type of object. 2. `undefer EXPR` is exactly the same as `EXPR`, except that if `EXPR` evaluates to a deferred object, it gets called (in the sense of it being equivalent to a lambda which can be called). Here's a prototype implementation, and a demonstration of how it would be used to implement late bound arguments. Please note, I understand that the syntax here is horrible. That's exactly the point, this needs language support to be non-horrible. That's what a "deferred expression" proposal would provide. # Explicitly creating Deferred objects is horrible, this is the bit that *really* needs language support class Deferred: def __init__(self, callable): self.callable = callable # This could easily be a builtin function (or an operator if people prefer syntax) once we have deferred objects. def undefer(expr): if isinstance(expr, Deferred): return expr.callable() return expr x = 12 # def f(a=defer x): def f(a=Deferred(lambda: x)): a = undefer(a) return a assert f(9) == 9 assert f() == 12 x = 8 assert f() == 8 assert f(9) == 9 If anyone wants to take this and make a *proper* deferred object proposal out of it, then please do so. If not, then at a minimum I think this offers something vaguely concrete to discuss regarding the "why deferred objects are a more general solution to the late bound argument" question. Paul