On Thu, Aug 10, 2017 at 01:25:24PM -0700, Chris Barker wrote:
> On Thu, Aug 10, 2017 at 8:39 AM, Paul Moore <p.f.mo...@gmail.com> wrote:
> 
> 
> >  Also, there's a potential issue
> > here - consider
> >
> >     [expr for var in even_numbers() if is_odd(var) while var < 100]
> >
> > This is an infinite loop, even though it has a finite termination
> > condition (var < 100), because we only test the termination condition
> > if var is odd, which it never will be.

I'm not sure why Paul thinks this is an issue. There are plenty of ways 
to accidentally write an infinite loop in a comprehension, or a for 
loop, already:

[expr for var in even_numbers()]

will do it, if even_numbers is unexpectedly an infinite iterator. Or you 
could write:

for num in even_numbers():
    if is_odd(num) and num > 100:
        break

No loop syntax, whether it is functional style (takewhile, map, etc.), 
comprehension, or traditional style for loops, enables the programmer 
to avoid thinking about what they write.


> why is the termination only tested if teh if clause is True? Could then not
> be processed in parallel? or the while first....

Because we're following the standard Python rule of left-to-right 
execution. The while clause is tested only if the if clause is true 
because it follows the if clause.

I think that there's an argument to be made for the rule:

    We can have `if` in a comprehension, or `while`, but not both

in order to limit complexity. Analogy: 

(1) we intentionally limit the decorator @ syntax to a subset of 
expressions;

(2) likewise we intentionally allow (but don't encourage) monkey-
patching of Python classes only, not built-ins.

Just because we *can* allow arbitrary code combinations doesn't mean we 
*must*. We have a choice to say:

    "No, you cannot mix `if` and `when` in the same comprehension. 
    Why? Because we say so. Because it is confusing if you do."

I'd be okay with that rule.

But if we decide to allow arbitrary combinations of for/if/while in 
comprehensions, then I think we must keep the same left-to-right rule we 
have now. Currently we process multiple for/if clauses left-to-right:

    [expr for x in a if cond for y in b]

is equivalent to:

    for x in a:
        if cond:
            for y in b:
                expr

rather than moving the `if` to the end. If you want it at the end, put 
it there yourself. Adding `while` shouldn't change that. It would be 
crazy-complicated to have a rule:

    "the presence of a while means the comprehension is 
    processed in parallel"

or

    "all the while clauses are processed before (after?)
    the if clauses, regardless of their order of appearance."


> so maybe better to do:
> 
> [expr for var in even_numbers() while var < 100 if is_odd(var)]

Well sure, that's the *correct* way to write the code:

for var in even_numbers():
    if not (var < 100): break
    if is_odd(var):
        results.append(expr)

(for some definition of "correct" -- this is clearly an expensive way to 
generate an empty list.)

But in general one might wish to test the if or the while in either 
order.


> Maybe it's just me, but I would certainly expect the while to have
> precedence.

Does that apply to these idioms as well?

    while cond:
        if flag:
            ...

versus:

    if flag:
        while cond:
            ...


I would not expect them to be the same, and nor would I expect these to 
be the same:

    [expr for x in seq if flag while cond]

    [expr for x in seq while cond if flag]


> I guess I think of it like this:
> 
> "if" is providing a filtering mechanism
> 
> "while" is providing a termination mechanism
> 
>  -- is there a use case anyone can think of when they would want the while
> to be applied to the list AFTER filtering?

[process(n) for n in numbers while n > 0 if is_odd(n)] 

Halt on the first zero or negative number, regardless of whether it is 
even or odd, but process only odd numbers.



Paul: 
> > Obviously, this is a contrived example. And certainly "don't do 
> > that, then" is a valid response. But my instinct is that people are 
> > going to get this wrong - *especially* in a maintenance environment.

That's the argument for limiting comprehensions to either `if` or 
`while` but not both. And I actually would be okay with that -- 
especially if we leave open the possibility of relaxing the prohibition 
in the future.

But personally, I think that's under-estimating the ability of 
programmers to reason about loops. Of course a comprehension with 
multiple for/if/while clauses is hard to reason about, and we shouldn't 
*encourage* them, but we don't prohibit multiple for/if clauses. Why 
should `while` be held to a higher standard? If we allow people to shoot 
themselves in the foot by writing complex list comprehensions with ten 
`for` loops and seven `if` clauses, why should we baulk at allowing them 
a `while` clause as well?



-- 
Steve
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to