
On Fri, Dec 06, 2019 at 09:11:44AM -0400, Juancarlo Añez wrote: [...]
Sure, but in this case, it isn't a fragment of a larger function, and that's not what it looks like. If it looked like what you wrote, I would understand it. But it doesn't, so I didn't really understand what it was supposed to do, until I read the equivalent version using first/next.
Exactly my point.
Indeed, and I agree with that. But I still don't see what advantage there is to having a `first` builtin which does so little. It's a really thin wrapper around `next` that: calls iter() on its iterable argument supplies a default and then calls next() with two arguments I guess my question is asking you to justify adding a builtin rather than just educating people how to use next effectively. This is how I would implement the function in Python: def first(iterable, default=None): return next(iter(iterable), default) But there's a major difference in behaviour depending on your input, and one which is surely going to lead to bugs from people who didn't realise that iterator arguments and iterable arguments will behave differently: # non-iterator iterable py> obj = [1, 2, 3, 4] py> [first(obj) for __ in range(5)] [1, 1, 1, 1, 1] # iterator py> obj = iter([1, 2, 3, 4]) py> [first(obj) for __ in range(5)] [1, 2, 3, 4, None] We could document the difference in behaviour, but it will still bite people and surprise them. We could, I guess, eliminate the difference by adding the ability to peek ahead to the next value of an arbitrary iterator without consuming that value. This would have to be done by the interpreter, not in Python code, and it would open new complexities. Consider a generator which yields values which depend, in some complex and unpredicatable way, on *when* it is called. Say, the amount of disk space available, or the number of records in a database, or the current time. If I peek into the generator, I would see the time at the moment I peeked: now = get_current_time_generator() peek(now) # returns 11:25:30am Since peek can't literally see into the future, it cannot be otherwise. But what happens when I call next? time.sleep(60) next(now) # what will this return? There are two alternatives: 1. `next(now)` will return 11:25:30am, the same value that peek gave; 2. `next(now)` will return 11:27:30am, the current time. Option 1 keeps the invariant that peeking will show you the next value without advancing the iterable, but it violates the invariant that `next(now)` yields the current time. Option 2 keeps the `next` invariant, but violates the `peek` invariant. Whichever option we choose, peeking into arbitrary iterators will break somebody's expectations. Bringing it back to `first`: * It seems to me that `first` adds very little that `next` doesn't already give us. * The simple and obvious implementation of `first` would have a troublesome difference in behaviour between iterator arguments and non-iterator arguments. * To eliminate that difference would require the ability to peek ahead into arbitrary iterators, including generators, which is a much bigger change, and equally troublesome. -- Steven