Introducing Sequence.last?

It occurred to me that although it has a first(where:) method, Sequence does not have a last(where:) method. It's true that Sequence isn't guaranteed to be finite, but most Sequence methods already assume finite sequences: min, max, compactMap, flatMap, map, reduce, sorted, reversed, joined, allSatisfy, shuffled, split and possibly more won't terminate if the Sequence is not finite already. Unless there was another justification for its omission, I'd volunteer to add it.

Shouldn't it at least be at BidirectionalCollection level?

Nvm, BidirectionalCollection already has last(where:).

first(where:) already might have to traverse the whole Sequence, so it's not unreasonable to have a last(where:) from a performance point of view. It's only if people would expect it to be efficient if the matching item is at the end that there'd be a problem.

2 Likes

I think the reason at this stage it wasn’t included was for potential stream and unending sequence usage, where there may be no defined end, and this type of API would run infinitely, unable to find the end.

It makes sense on Collection, certainly, where the API contract requires a defined count.

This plays into earlier discussions about potentially collapsing Sequence and Collection into one protocol, which seem to have fizzled out somewhat.

1 Like

Is there anything special about last and concerns of infinite sequences, versus any of the majority of other Sequence methods which already require the sequence to be finite?

1 Like

It’s a very fair point. I think a lot of the API on Sequence now tends to assume finite-ness, eg max which cannot be calculated unless the sequence is finite.

Again, this plays into the collapsing API together discussion. I’m certainly not against it, nor do I really think the reasoning I mentioned holds much water any more considering the precedence set by the many other methods in the API.

1 Like

Worth keeping in mind first(where:) has the same behaviour, namely it doesn't ever return for an infinite Sequence full of elements failing to match the predicate.

These APIs had the potential to become infinite but if you couldn’t assure the “where” predicate would eventually hit a winner, you could say it was a programmer error.

In the case of last(where:), you would necessarily need an end because you’d need to check all ones after the last matching item until the end. This therefore needs an end - otherwise you’d keep checking infinitely that each later element did not pass the predicate.

Also, the concept of Sequence was that the iteration may be destructive (if you wanted a guarantee otherwise, you’d use Collection), and consume elements in the sequence, removing them upon iteration. Last-related API would ensure you would always consume the entire sequence, and leave nothing left. First-related API doesn’t do this, and would consume only until the point you specify.

3 Likes

It wouldn't be a programmer error to search for a user-provided term which doesn't match. What you're doing that in an infinite sequence for, however...

Providing a predicate you’re not certain will ever match a first to an unending sequence seems like a terrible idea that as a programmer you should know “this would not end well” because... it may never end... I would see this as a bad programming choice, just like any other infinite-loop behaviour, and could be seen as a programmer error. You wouldn’t generally be given a sequence without knowing “hey, this doesn’t end”. It would be part of whatever API you’re using, eg an InfiniteStream API, and so would be clear “not a good idea here”.

I was more pointing out the fact this API does not inherently require an end and exhaustively iterating by design. Last-based API however does.

I think this just speaks to how mushy the definition of a sequence is. I really don't understand what it is. There's methods that only work for finite sequences, but not all of them. It needs to be able to make an iterator, but there's no guarantee the iterator won't "use up" the sequence and make it one shot.

If we accept that we're okay with having "usable, but potentially infinitely running" methods on Sequence, that makes me wonder what the difference is from Collection. Collection has a count, but you can't really do anything useful with it (unless you're dealing with a RandomAccessCollection).

I would appreciate if someone could clear up what exact Sequence is meant to model, compared to IteratorProtocol and Collection.

6 Likes

You're right that Sequence is very unclear. We've discussed removing it in the past:

This snippet is particularly relevant to this discussion: