On Wed, Dec 11, 2019 at 05:20:13PM +1100, Chris Angelico wrote:
> On Wed, Dec 11, 2019 at 5:14 PM Steven D'Aprano <st...@pearwood.info> wrote:
> >
> > On Tue, Dec 10, 2019 at 07:21:13PM -0600, Tim Peters wrote:
> > > While the meaning of `first()` is clear for any iterable argument.
> >
> > Sorry Tim, I have to disagree. The meaning of `first` is:
> >
> >     return the first element of a sequence or container (in standard
> >     iteration order), OR the *next* element of an iterator
> >
> > and I don't think that this is even a little bit clear from the name.
> >
> 
> You know that cliche about how today is the first day of the rest of
> your life?

There's a difference between the first day of *the rest* of your life 
and the first day of your life.

There's a difference between the first item of *the rest* of the 
iterator and the first item of the iterator.

Cliches and platitudes will only take you so far. What counts here is 
the behaviour of the code. When you iterate over an iterable using a for 
loop, you get the same sequence of items whether it is an iterator or 
not. But that's not what happens if you call `first(iterable)` multiple 
times. Calling it once is fine, but people will call it multiple times.

I am writing some code as we speak to process a bunch of lines of text 
from an iterable. I'm expecting a mandatory header as the first line, an 
optional line of dashes "-------", and then one or more lines that need 
processing. If all I remembered is that `first` works on both iterators 
and non-iterators, I might write something like this:


    def process(lines):
        # can't use next
        header = first(lines, '')
        line = first(lines, '')
        if is_dashes(line):
            line = first(lines, '')
        while line:
            do_stuff(line)
            line = first(lines, '')


Seems reasonable, if you read "first" as meaning "first line in the 
remaining iterator". But as soon as I pass a concrete sequence of lines, 
rather than an iterator, I'll have an infinite loop.
        
Let me try to anticipate your likely objection:

    "Well of course it's not going to work, you're calling `first` 
    instead of `next`. When you want to consume items, you should
    call `next`."


Okay, but isn't it your position that the difference between "first" and 
"next" is a difference that makes no difference? (See quote below.)

Obviously I could re-write that function in many ways, but the simplest 
fix is to call `lines = iter(lines)` at the top of the function.

But if I do that, I don't need "first", I can just use `next`, which 
reads better. What does "first" give me?

I know, I can use it to look-ahead into a sequence:

    head = first(lines)  # like lines[0] but works if it's empty
    if head is None:
        print("empty")
    else:
        do_stuff(lines)


Works fine... until I pass an iterator, and then wonder why the first 
line is skipped.

The thing is, we're fooled by the close similarity of iteration over 
iterators and other iterables (sequences and containers). Destructive 
iteration and non-destructive iteration is a big difference. Utility 
functions like the proposed `first` that try to pretend there is no such 
difference are, I believe, a gotcha waiting to happen.


> Your life is an iterator.

Speak for yourself. My life is a box of chocolates.


> "Next" and "first" are basically
> synonymous when you can't go backwards. IMO the distinction you
> describe here isn't actually significant at all - either way, you get
> the first element of "whatever remains", and the only difference is
> whether it's nondestructive (with most containers) or destructive
> (iterators).


-- 
Steven
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/2DREAFZQEE6ZMWFB2OMUN5XDTQMPGKB6/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to