[swift-evolution] [Manifesto] Completing Generics
plx
plxswift at icloud.com
Thu Mar 3 07:47:14 CST 2016
Thanks for publishing the manifesto; for the most part everything seems reasonable and logical extensions that would “complete" the current system.
I do have a few remarks and I’m going to put them each in a separate email (they don’t relate to each other).
# Comment: Variadic Generics “Need” Structural Union Types
Disclaimer: I’m not 100% sure on the terminology; I’m using “structural union” here to mean an “anonymous enum” (like “A | B | C | D”) wherein both (a) the order matters (e.g. `(A | B | C | D)` and `(D | C | B | A)` are different types) and also (b) types can repeated (e.g. `(Int | Int | Int)` exists and isn’t just `Int`). If that’s not a "structural union” I apologize for the inappropriate terminology.
Content: I think variadic generics will wind up being rather limited in their utility w/out a correspondingly-variadic form of “structural union”; the reason is that for many type-combinators the right representation for some of the resulting associated types is a sum.
Here’s a motivating example: a read-only collection acts as the concatenation of its `n` input collections. Today this requires a family of types (one per `n` you wish to support) and would superficially seem like a good place to use variadic generics...but it’s not so straightforward:
// non-variadic version:
struct ChainCollection3<A:Collection,B:Collection,C:Collection> : Collection {
let a = A
let b = B
let c = C
}
enum ChainCollection3Index<A:Collection,B:Collection,C:Collection> : ForwardIndex {
case IndexA(A.Index) // <- can’t be just `A` due to name collision!
case IndexB(B.Index) //
case IndexC(C.Index) //
}
// showing the index in use:
extension ChainCollection3 {
associatedtype Index = ChainCollection3Index<A,B,C>
var startIndex: Index { return .IndexA(a.startIndex) }
var endIndex: Index { return .IndexC(c.endIndex) }
// the below isn’t actually correct b/c it doesn’t
// properly handle the case when one of the constituent
// collections is empty…also assuming collections-move-indices:
func successorFor(index: Index) -> Index {
switch (index) {
case let .IndexA(aIndex):
return aIndex == a.endIndex ? .IndexB(b.startIndex) : .IndexA(a.successorFor(aIndex))
case let .IndexB(bIndex):
return bIndex == b.endIndex ? .IndexC(c.startIndex) : .IndexB(b.successorFor(bIndex))
case let .IndexC(cIndex):
precondition(cIndex != c.endIndex)
return .IndexC(c.successorFor(cIndex))
}
}
// and so on, mostly obvious boilerplate
}
…and although a good variadic generics system could eliminate the need for the family like ChainCollection*, it’s hard to see how you could implement an `Index` type in the variadic setting—without which you are “capped” at the `Sequence` level—w/out also having *variadic* structural unions (I think the best you could *maybe* do is manually implement the equivalent of that enum as e.g. a managed buffer with a type tag and a lot of casting, but you wouldn’t *want* to do that...).
Perhaps I’m missing a trick here, but if I’m not it does seem like the lack of variadic structural unions places a pretty low ceiling on what you could build using variadic generics.
More information about the swift-evolution
mailing list