On 04/26/2018 12:12 PM, Tim Peters
wrote:
[Larry
Hastings <larry@hastings.org>]
I hate to be pedantic--there's enough of that going on in this thread--but I
can't agree with the word "simplifed" above. I agree that the code using
binding expressions is shorter. But considering that emit the two code
examples implement the exact same algorithm, to the point where their
bytecode would look nearly* identical, ISTM that the two code examples are
of identical complexity.
In the absence of defining an objectively computable complexity
measure, I expect you're doomed to arguing taste.
As are you! I haven't seen any arguments that binding expressions
allow us to express programs that were inexpressible in Python
before. I'm not even sure that binding expressions fall under the
heading of "syntactic sugar", given their negligible semantics (and,
imo, negligible benefit). What else is left, on both sides
of the debate, if not a debate over aesthetics?
For example, argue
that both spellings have the same formal "cyclomatic complexity"
measure (which they do). By other formal measures (e.g., total number
of identifier instances), the latter spelling is "objectively
simpler". By yet others (e.g., total number of non-whitespace
characters divided by total number of lines), the former spelling is
"objectively simpler".
What is this "objective simplicity" measurement you cite? I
understand that the code example cited had fewer identifiers, so
when measuring "number of identifiers used" in isolation, the code
example using binding expressions had fewer of them. But this is so
narrow as to be almost meaningless.
Perhaps I'm misunderstanding you, but I read this as saying that
there's a larger, well-established concept called "objective
simplicity", of which this measurement is a part. Can you tell me
more about it? Google was no help here.
But that all kinda misses the point to me: the latter spelling is
"obviously simpler" in a way that _actually matters_, for the same
reason, e.g., a case statement with N cases is "obviously simpler"
than the semantically equivalent spelling using N nested if/else
if/else if/else if/else ... blocks.
As I already mentioned, the with-binding-expressions code expresses
the same code, the same concept, and likely results in the same
bytecode, as the without-binding-expressions code. In contrast, a
switch statement is simpler than a series of nested if
statements. It's a different code construct, it has different (and
comparatively restricted) semantics, and it results in simpler (and
faster) code. Whereas the with-binding-expressions code is
equivalent to the without-binding-expressions code, semantically,
bytecode-ly, etc. So comparing the with-binding-expressions version
of the code to the simplification afforded by a switch statement
isn't an apples-to-apples comparison.
In other words: you're really only arguing taste here. You find it
"obviously simpler", but this an aesthetic call on your part and not
an objective measurement. Me, my tastes are different--I find it
"needlessly complicated" and prefer the without-binding-expressions
version.
If it weren't for that you hate being pedantic, I'd add that you're
overlooking the piles of leading whitespace characters also saved in
the latter ;-) The number of those saved grows quadratically in the
number of uselessly indented blocks shifted left.
Surely my dislike of being pedantic is irrelevant to your being
pedantic. It seems it's not something you mind doing! ;-)
The nightmare scenario of quadratically right-shifted code is of
course solvable in other ways. Off the top of my head, one could
rewrite the code example as follows, without any new syntax:
def r(value):
nonlocal reductor
reductor = value
return value
if r(dispatch_table.get(cls)):
rv = reductor(x)
elif r(getattr(x, "__reduce_ex__", None)):
rv = reductor(4)
elif r(getattr(x, "__reduce__", None)):
rv = reductor()
else:
raise Error("un(shallow)copyable object of type %s" % cls)
It makes this particular code example longer--however it avoids the
indenting you seem to dislike. However: given that "r()" is shorter
than "reductor := ", the more elifs you add, the more overall characters
you'll save! With another three or four elifs this version would
probably use fewer characters overall.
I'm not seriously arguing that people rewrite their code in this
way; I think it's less clear this way. I suppose my point is, "we
already have so many ways To Do It, I'm not in favor of adding
syntax to the language to add one more way".
and the density of complexity per line has shot up.
Average non-whitespace character count per line has certainly shot up,
but I don't actually know what you mean by "density of complexity"
there.
What I meant was, stuff-accomplished-per-line. I guess we could
measure that objectively as bytecodes / linecount. But I meant it
more as a subjective measurement, considering factors like "how many
unrelated operations occur on this line?" and "how many operations
on this line have side-effects?". I suggest that the
with-binding-expressions version has more side effects and more
unrelated operations per line, because several lines now fold
assignment (side effect) into lines that were previously flow
control.
/arry