On 2014-03-04, at 13:07 , Chris Angelico <rosuav@gmail.com> wrote:
On Tue, Mar 4, 2014 at 10:09 PM, Steven D'Aprano <steve@pearwood.info> wrote:
Agreed. What I have in my head is some vague concept that the Python evaluation rules will somehow know when to evaluate the thunk and when to treat it as an object, which is (as I understand it) what happens in Algol. Again, just thinking aloud, perhaps we do this:
thunk = `some_expression` # delays evaluation a = [0, 1 + thunk] # evaluates thunk in the current scope b = [0, `1 + thunk`] # delays evaluation and creates a thunk object # equivalent to `1 + some_expression` c = b[1] # now evaluates the thunk object d = f(2, thunk) # evaluates thunk in f's scope e = g(3, `thunk`) # passes the un-evaluated thunk object to g
Consider this just a sketch, and in no way fully thought out.
(This will most definitely need a PEP.)
PEP can come later. First, let's get some solid use-cases, and start looking at implications. The way it's described here, there's effectively magic when you try to look at an object of this type, which will break a lot of assumptions. Most people expect that:
foo = bar assert foo is bar
to be a safe assumption, but if bar is a thunk, then it's getting evaluated separately in each of those
Why? Either it's forced during assignment or both names map to the same thunk, and are both forced when any of them is. That could be during the identity check, but since both names refer to the same thunk they can only yield the same value, so the identity check needs not force the thunk. An equality test would likely force the thunk.
so that might cause confusion. But you could always special-case it: writing `thunk__` will be guaranteed to transmit the thunk unchanged, and if you actually mean to add another wrapper layer, use `(thunk__)` or something instead.
Is there a use case for actually thunking a thunk?
The biggest thing to figure out is scoping. Does a thunk take a snapshot of its enclosing scope (a closure), or is it an expression that gets evaluated in the target namespace? The latter could be extremely confusing, but the former's just what a nested function does, so this'd just be a new lambda syntax.
That is essentially what a thunk is, at least in my experience: it is conceptually a nullary memoized function, forced (evaluated/called) if an actual value/object is ever needed but potentially thrown out during reduction. The difference, under the proposed semantics, is that the forcing of the thunk would be implicit where that of a function is explicit (not sure that's a good idea in a strict language).