On 4 October 2016 at 08:56, Stephen J. Turnbull
These are my opinions; I don't claim any authority for them. I just don't find the proposed syntax as obvious and unambiguous as you do, and would like to explain why that is so.
Ken Kundert writes:
In my experience it is exceptions and inconsistencies that consume 'working memory in the brain of humans'. By eliminating the distinction between list comprehensions and for loops we would be making the language simpler by eliminating an inconsistency.
I don't think of a comprehension as a for loop, I think of it as setbuilder notation (although of course I realize that since lists are sequences it has to be a for loop under the hood). So the claimed inconsistency here doesn't bother me. I realize it bothers a lot of people, but the proposed syntax is not obvious to me (ambiguous and inconsistent in its own way).
[T]he writing of the generator function represents a speed bump.
It used to be, for me, but it really isn't any more. Perhaps you might get used to it if you tried it. Harder to argue: the fact that Guido and Nick (inter alia) consider it good style to use named functions makes that point a hard sell (ie, you don't need to convince me, you need to convince them).
Whereas writing something like the following is simple, compact, quick, and obvious. There is no reason why it should not be allowed even though it might not always be the best approach to use:
for i in range(5) for j in range(5) for k in range(5): ...
To me, that is visually ambiguous with
for i in (range(5) for j in (range(5) for k in range(5))): ...
although syntactically the genexp requires the parentheses (and in fact is almost nonsensical!) I could easily see myself forgetting the parentheses (something I do frequently) when I *do* want to use a genexp (something I do frequently), with more or less hilarious results. As already mentioned:
for i, j, k in itertools.product(range(5), range(5), range(5)): ...
To me that is much clearer, because it expresses the rectangular shape of the i, j, k space. I would also stumble on
for i in range(5) for j in range(i + 1): ...
at least the first few times I saw it. Based on the English syntax of "for" (not to mention the genexp syntax), I would expect
for j in range(i + 1) for i in range(5): ...
If itertools.product is the wrong tool, then the loop bodies are presumably complex enough to deserve new indent levels. Note that simple filters like non_nil (see below) can easily be used, as long as the resulting set is still a product.
And I would really like to be able to write loops of the form:
for item in items if item is not None: ...
def non_nil(items): return (item for item in items if item is not None)
for item in non_nil(items): ...
I think that's very readable, so the only reason why that 2-line function needs to be syntax that I can see is your distaste for defining functions, and that of other Python programmers who think like you.
Again this is just personal opinion, but I agree 100% with everything Stephen said. It *is* a stumbling block to get used to writing generator functions like non_nil() above, but it's also a worthwhile learning experience. And yes, it's somewhat inconvenient to do so if you're working in the standard Python REPL, but designing language features around REPL usage isn't (IMO) the right choice. And if you really need a better way of handling that sort of refactoring in an interactive environment, tools like the Jupyter notebook are probably what you're looking for. (Trying to collapse multiple clauses into one line/statement is something Perl was famous for, and it's in many ways quite an attractive feature. But IMO it directly contributes to Perl's reputation for unreadability, because it *does* get used too much, whether you think it will or not - one person's "nicely compact" is another person's "obfuscated". So I'm glad that Python's design avoids encouraging that style - even though I do occasionally remember fondly my Perl one-liners :-)) Paul