On Mar 4, 2014, at 14:31, Greg Ewing email@example.com wrote:
Steven D'Aprano wrote:
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.
But Algol has the benefit of static typing -- the procedure being called explicitly declares whether the argument is to be passed by name or value. Python has no idea about that at compile time.
This is the main reason I think it's more productive to think of this in terms of Lisp-style quoting than Algol-style thunks. The fact that quoting/thunking gives you a code object instead of an AST (sexpr) is not significant here* (unless we're also considering adding macros). The fact that it gives you a first-class value, and that we can't use the "implicit casting" syntax that comes with static typing to magically evaluate the object at the right time, is critical.
This is basically the same problem I described trying to implement Boost-style auto lambdas in Python without C++-style implicit cast from lambda to function.
* It is significant _elsewhere_. In an AST, "b" is just a reference to a name; in a code object, we have to have already determined whether it's a reference to a fast, closure, or global name--unless you want to do something like automatically compile `b` as if it were something like vars('b'), or to do something equivalent like add a LOAD_DYNAMIC opcode.
b = [0, `1 + thunk`] # delays evaluation and creates a thunk object # equivalent to `1 + some_expression` c = b # 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
There's really no way to cleanly distinguish the c, d, and e cases. There are really only two possibilities: Magic evaluation everywhere (so d is passing in eval(thunk) to f), or explicit evaluation (whether via eval or otherwise) only (so c is just binding the unevaluated thunk). I think either of those is potentially viable, but I don't think there's anything in between.
Note that you can always quote the implicit evaluation (if you go the first way) or explicitly eval anywhere you want (the second), so neither one really limits what you can do.
When exactly does implicit evaluation of a thunk object occur? Does `b` give you an unevaluated thunk object? What if b is a custom sequence type implemented in Python -- how does its __getitem__ method avoid evaluating the thunk object prematurely?
You're creating a new thunk/quoting a new expression inside the backticks, so the issue doesn't arise. You're creating a new code object that looks up b and indexes it. The __getitem__ call doesn't happen until that new code object is evaluated.
None of these problems occur in Algol, because its thunks are not first-class values (you can't store them in arrays, etc.) and it uses static type information to tell when to create and evaluate them.