[Python-ideas] PEP 572 version 2: Statement-Local Name Bindings
Brendan Barnwell
brenbarn at brenbarn.net
Fri Mar 2 13:30:38 EST 2018
On 2018-03-02 03:43, Chris Angelico wrote:
> After dozens of posts and a wide variety of useful opinions and
> concerns being raised, here is the newest version of PEP 572 for your
> debating pleasure.
After following the discussion here, I think I've come to the
conclusion that something like "EXPR with NAME as VALUE" is better, and
that's because the "out-of-order" execution there is actually a benefit.
The example that gave me the most pause in this thread was Paul Moore's
involving the quadratic formula (slightly emended here :-):
x = [(-b + sqrt((b**2 - 4*a*c as D))/(2*a), (-b + sqrt(D)/(2*a)]
To me this is a classic example of the kind of thing I would want to do
with a statement-local binding, so whatever syntax is chosen won't be
useful to me if it doesn't nicely work in situations like this.
But I don't find the above example nice, for more or less the reason
Paul cited: the need to assign the expression "inline" (at the point
where it's first defined) creates an asymmetry that masks the fact that
it's the same expression being re-used elsewhere --- and it's exactly
that re-use that I would want to HIGHLIGHT with a statement-local binding.
The alternative with the name binding at the end is much better:
x = [(-b + sqrt(D))/(2*a), (-b - sqrt(D))/(2*a) with b**2 - 4*a*c as D]
To my mind, it's a real desideratum for a statement-local binding that
it should pull the bound expression OUT of the enclosing context. The
overall statement gains in readability only if the reader can easily see
a shared element across multiple parts. If I can't tell at a glance
that the two roots both involve the same value D, there's little point
in having a statement-local binding for it. (It still may gain in
computational cost, since the expression won't have to be evaluated
multiple times, but I see that as a much less important benefit than
readability.)
Also note that the version above comes perilously close to the existing
solution with a regular local variable:
D = b**2 - 4*a*c
x = [(-b + sqrt(D))/(2*a), (-b - sqrt(D))/(2*a)]
The only difference is that now D is "leaked" to following code. Nick
Coghlan has argued that there's no point in an inline-assignment
construct if it's not somehow local, since a big part of its purpose is
to simplify reasoning by avoiding any stomping on "real" local variables.
But if that's the case, maybe what we want is actually another thing
that's been discussed many times on this list, namely something like a
with-block that can define "super-local" variables that disappear at the
end of the block:
with b**2 - 4*a*c as D:
x = [(-b + sqrt(D))/(2*a), (-b - sqrt(D))/(2*a)]
This the same as my earlier version with "with", except the with clause
comes at the beginning.
There's no need to go into extreme detail here on these proposals as
they're not really what's proposed by this PEP. But my point is that,
from my perspective, they have something crucial that the current
proposal lacks: they explicitly separate the *definition* of the shared
expression from its *use* within the statement. Having to do the
name-binding inline at the place where the re-used expression happens to
occur makes the overall construct LESS readable for me, not more, so I'm
-1 on the current proposal.
--
Brendan Barnwell
"Do not follow where the path may lead. Go, instead, where there is no
path, and leave a trail."
--author unknown
More information about the Python-ideas
mailing list