On Tue, Dec 10, 2019 at 1:50 PM Tim Peters <tim.peters@gmail.com> wrote:
[Brett Cannon <brett@python.org>]
> Thinking out loud here...
>
> What idiom are we trying to replace with one that's more obviously and whose
> semantics are easy to grasp?

For me, most of the time, it's to have an obvious, uniform way to
spell "non-destructively pick an object from a container (set, dict,
list, deque, heap, tuple, custom tree class, ...)".  I don't even have
iterators in mind then, except as an implementation detail.  For that
reason, raising `StopIteration` if the container is empty grates.  The
_value_ (the state of the container) I passed is inappropriate then,
so more-itertool's ValueError makes more sense to me.

Fair enough. I will fully admit that while I'm currently learning Clojure the fact they have such a uniform approach to containers is enviable, hence why this discussion interests me. :) (Although I was already a functional fan.)
 

The toolz.itertoolz version of `first()` differs.  That one is just
next(iter(argument)).  No default.  I like the more-itertools flavor
better.

As to which idiom it intends to replace, _that's_ the annoyance being
addressed:  there is no "one obvious way to do it" now.  Although for
each individual container type, there's sometimes an obvious way to do
it for objects of that type (e.g., object[0] for a list or heap, or
object.root for a rooted tree class).

> `first(iterable)` that raises is StopIteration is `next(iter(iterable))`. `first(iterable)` that
> defaults to None and doesn't raise is `next(iter(iterable), None)`. Now only if the raised
> exception changes do you end up with something like Tim's examples where more
> than one line is definitely needed.

Talking about "needed" is treating this like an axiomatic system where
redundancy is in Very Bad Taste.

Sorry, "needed" was too strong of a word. It's more about justification for including in the stdlib and deciding to support it for a decade or more versus the answer we give for simple one-liners of "put in your personal toolbox if you don't want to type it out every time".
 
  But, to the contrary, in functional
languages the _implementers_ think very hard about creating a minimal
core, but the user interface supplies everything _useful_ and
sometimes doesn't even note whether a thing is part of the minimal
core.

Yep, and the general abstraction to a universally applicable core is very nice.
 

When I want `first()`, I want `first()`.  I don't care how it's
implemented, and I couldn't care less that I _could_ write it myself
by composing other functions in a brief one-liner.

Sure, but the question I think that this thread and me are proposing are what "first()" means to everyone. I think you and I are on the same page, but it's a question as to whether others are as well. :)
 

> So I think the question is what problem are we trying to solve here? Is it the lack of
> knowledge of the 2-argument next() form? Or is it that people are regularly wanting
> the first item from an iterable and when it doesn't exist they want to raise an
> exception different from StopIteration (and what is that alternative exception)?
>
> If it's the former then I think the case could be made that more education of the
> one-liner might be all that's needed here. Now Guido did the research and showed
> that the stdlib doesn't seem to realize this form really exists, so it might be quite
> the education. ;)

`first()` definitely isn't _needed_.  Without it, people will continue
reinventing their own ad hoc methods of getting it done, and they'll
succeed.

> But if it's the latter then there's a greater savings in complexity from providing first()
> with those semantics. But once again the question becomes how often does that
> come up?

Often enough that both relevant packages (more-itertools and
toolz.itertoolz) have supplied it for years, although with different
endcase behavior.  Certainly not often enough to merit being
__bulitin__.

I agree.
 

> I obviously have no answers to provide. :) My gut is suggesting that if it's the one-liner
> replacement it might not be worth it, but if it's to raise a different exception I could see
> more of a use for adding something to the stdlib.

As above, `first()` is an atomic concept in my head.  It _can_ be
synthesized out of more basic concepts, but in the ways I think about
getting a problem solved, it's a primitive.  As a primitive, passing
an empty argument is a ValueError in the absence of also passing an
explicit default to return in that case.

Fair enough.
 

I can live without it, but that's not really the point ;-)

:)