For what it's worth, the SQLite wrapper GRDB needed a way to iterate database results. Since SQLite can throw an error at any point during iteration (I/O, etc.), Sequence and Iterator did not fit the bill. They can't throw during iteration. I thus had to define Cursor, a protocol for "types that supply the values of some external resource, one at a time."
Relevant excerpt from its documentation:
/// Cursors share traits with lazy sequences and iterators from the Swift
/// standard library. Differences are:
///
/// - Cursor types are classes, and have a lifetime.
/// - Cursor iteration may throw errors.
/// - A cursor can not be repeated.
///
/// The protocol comes with default implementations for many operations similar
/// to those defined by Swift's Sequence protocol: `contains`, `dropFirst`,
/// `dropLast`, `drop(while:)`, `enumerated`, `filter`, `first`, `flatMap`,
/// `forEach`, `joined`, `joined(separator:)`, `max`, `max(by:)`, `min`,
/// `min(by:)`, `map`, `prefix`, `prefix(while:)`, `reduce`, `reduce(into:)`,
/// `suffix`.
public protocol Cursor: class {
/// The type of element traversed by the cursor.
associatedtype Element
/// Advances to the next element and returns it, or nil if no next element
/// exists. Once nil has been returned, all subsequent calls return nil.
func next() throws -> Element?
}
I had to fully rewrite lazy throwing versions of map
, filter
, etc: 773 lines (!) of support code without any help from the standard library. Not a funny job. But it was necessary for a library that claims to be "a toolkit for SQLite databases, with a focus on application development", and thus needs to come with many batteries included. Expressive, fluent, efficient (and above all robust) consumption of lazy database results was part of the design goals.