
On 8 June 2015 at 21:24, Andrew Barnert <abarnert@yahoo.com> wrote:
Now you really _are_ reinventing let. A let expression like this:
x = let b=a.b in (b if b else a.c)
... is effectively just syntactic sugar for the lambda above.
Sure, I've thought a *lot* about adding let-type syntax - hence PEP's 403 (@in) and 3150 (given) for a couple of variations on statement level local variables. The problem with a let expression is that you still end up having to jumble up the order of things, just as you do with the trick of defining and calling a function, rather than being able to just name the subexpression on first execution and refer back to it by name later rather than repeating the calculation. Thus a let expression doesn't actually help all that much with improving the flow of reading or writing code - you still have the step of pulling the subexpression out and declaring both its name and value first, before proceeding on with the value of the calculation. That's not only annoying when writing, but also increases the cognitive load when reading, since the subexpressions are introduced in a context free fashion. When the named subexpressions are inlined, they work more like the way pronouns in English work: When the (named subexpressions as they) are inlined, they work more like the way pronouns in English work. It's a matter of setting up a subexpression for a subsequent backreference, rather than pulling it out into a truly independent step.
And it's a lot more natural and easy to reason about than letting b escape one step out to the conditional expression but not any farther. (Or to the rest of the complete containing expression? Or the statement? What does "x[(a.b as b)] = b" mean, for example? Or "x[(b if (a.b as b) else a.c) + (b if (d.b as b) else d.c)]"? Or "x[(b if (a.b as b) else a.c) + b]"?)
Exactly, that's the main problem with named subexpressions - if you let them *always* leak, you get some very confusing consequences, and if you *never* let them leak, than you don't address the if statement and while loop use cases. So to make them work as desired, you have to say they "sometimes" leak, and then define what that means in a comprehensible way. One possible way to do that would be to say that they *never* leak by default (i.e. using a named subexpression always causes the expression containing them to be executed in its own scope), and then introduce some form of special casing into if statements and while loops to implicitly extract named subexpressions.
As a side note, the initial proposal here was to improve performance by not repeating the a.b lookup; I don't think adding an implicit comprehension-like function definition and call will be faster than a getattr except in very uncommon cases. However, I think there are reasonable cases where it's more about correctness than performance (e.g., the real expression you want to avoid evaluating twice is next(spam) or f.readline(), not a.b), so I'm not too concerned there. Also, I'm pretty sure a JIT could effectively inline a function definition plus call more easily than it could CSE an expression that's hard to prove is static.
Yes, I'm not particularly interested in speed here - I'm personally interested in maintainability and expressiveness. (That's also why I consider this a very low priority project for me personally, as it's very, very hard to make a programming language easier to use by *adding* concepts to it. You really want to be giving already emergent patterns names and syntactic sugar, since you're then replacing learning a pattern that someone would have eventually had to learn anyway with learning the dedicated syntax). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia