Steve Jorgensen wrote:
I think I have an idea how to do something like what you're asking with less magic, and I think an example implementation of this could actually be done in pure Python code (though a more performant implementation would need support at the C level). What if a deferred object has 1 magic method ( __isdeferred__ ) that is invoked directly rather than causing a thunk, and invocation of any other method does cause a thunk. For the example implementation, a thunk would simply mean that the value is computed and stored within the instance, and method calls on the wrapper are now delegated to that. In the proper implementation, the object would change its identity to become its computed result.
I haven't had any replies to this, but I think it warrants some attention, so I'll try to clarify what I'm suggesting.
Basically, have a deferred object be a wrapper around any kind of callable, and give the wrapper a single method __is_deferred__ that does not trigger unwrapping. Any other method call or anything else that depends on knowing the actual object results in the callable being executed and the wrapper object being replaced by that result. From then on, it is no longer deferred.
I like this idea because it is very easy to reason about and fairly flexible. Whether the deferred object is a closure or not depends entirely on its callable. When it gets unwrapped is easy to understand (basically anything other than assignment, passing as an argument, or asking whether it is deferred).
What this does NOT help much with is using for argument defaults. Personally, I think that's OK. I think that there are good arguments (separately) for dynamic argument defaults and deferred objects and that trying to come up with 1 concept that covers both of those is not necessarily a good idea. It's not a good idea if we can't come up with a way to do it that IS easy to reason about, anyway.