Add Various Zip-Related Types and Operations

A preliminary implementation can be found here (as a package).

Here's an overview of all available conformances on the most important types along with some preliminary motivation.

Zip2Sequence / ZipLongest2Sequence

: where Sequence1 / 2:
Sequence Sequence
LazySequenceProtocol LazySequenceProtocol

Currently, when Zip2Sequence's underlying Sequences are lazy, their laziness gets lost. By conforming to LazySequenceProtocol, we get rid of this regression.

Zip2Collection

: where Collection1 / 2:
Sequence Sequence
LazySequenceProtocol, LazyCollectionProtocol LazyCollectionProtocol
Collection Collection
BidirectionalCollection RandomAccessCollection
RandomAccessCollection RandomAccessCollection
MutableCollection MutableCollection
RangeReplaceableCollection RangeReplaceableCollection
ExpressibleByArrayLiteral RangeReplaceableCollection
ExpressibleByDictionaryLiteral RangeReplaceableCollection

Zip2Collection's protocol inheritance can roughly be split into four categories; support for: (1) laziness, (2) immutable and (3) mutable access and (4) literal initialisation.

  1. The arguments made for Zip2Sequence also stand here.

  2. The benefits of read-only access are obvious in terms of the extra performable operations and performance benefits. It also guarantees that it can be nondestructively consumed by iteration. Existing code might get performance benefits in certain unambigious situations as well "for free" since some of Sequence's operations have more performant default implementations up the hierarchy (note to self: check if this truly is the case).

  3. Zip2Collection's conformance to RangeReplaceableCollection means +(_:_:) will become available for zips.

    This will need strong use-cases.

  4. The main reason for ExpressibleByArrayLiteral and ExpressibleByDictionaryLiteral conformance is to provide an alternative shorthand for creating a default value for when Zip2Collection is Optional or empty:

    _ = Zip2Collection<[Int], [Int]>.none ?? [(42, 42)]
    _ = { $0.isEmpty ? [42: 42] : $0 }(zip([Int](), [Int]()))
    

    This will need stronger argumentation (or be dropped from the proposal). Also discussed here.

ZipLongest2Collection

: where Collection1 / 2:
Sequence Sequence
LazySequenceProtocol, LazyCollectionProtocol LazyCollectionProtocol
Collection Collection
BidirectionalCollection RandomAccessCollection
RandomAccessCollection RandomAccessCollection

ZipLongest2Collection is more limited than Zip2Collection as it can't guarantee MutableCollection and RangeReplaceableCollection's behavioural semantics (as far as I can see). The arguments made in points 1 and 2 (partly) in Zip2Collection also stand here.