<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">Thanks for getting the discussion started with such a thorough initial writeup.</div><div class=""><br class=""></div><div class="">My overall concern is this:</div><div class=""><br class=""></div><div class="">- on the one hand, I *very much* understand—and support—the idea of starting simple and adding functionality later</div><div class="">- on the other hand, this is *already* a rather complicated proposal syntactically, semantically, and probably also in implementation</div><div class="">- on the gripping hand, I’m concerned that despite being so complicated, it’s perhaps too limited to pay for itself as-proposed</div><div class=""><br class=""></div><div class="">Or, put differently, if you believe there’s an optimal point on the complexity/functionality for a v1, I worry a bit after reading it that it’s buying too little practical functionality for the complexity it’ll bring, and that something a *little* more complex might provide a *lot* more practical utility.</div><div class=""><br class=""></div><div class="">To that end, I’ve provided some concrete-ish examples of things it’d be nice to be able to do via variadics and the additional features they would either *need* or at least *benefit* from having available. </div><div class=""><br class=""></div><div class="">All of these are very much “nice to have” features that need not be included in a first version, but they each illustrate one possible complexity/capability trade-off.</div><div class=""><br class=""></div><div class="">A.1: Support for uniform variadic-parameter associated-type constraints?</div><div class=""><br class=""></div><div class="">Consider trying to write a variadic form of this sequence:</div><div class=""><br class=""></div><div class=""> // given sequences a, b, provides a sequence equivalent-to</div><div class=""> // zip(a,b).lazy.map() { </div><div class=""> // ( min($0.0,$0.1), max($0.0,$0.1) )</div><div class=""> // }</div><div class=""> struct ExtremaSequence<A:Sequence,B.Sequence where A.Iterator.Element == B.Iterator.Element, A.Iterator.Element: Comparable> {</div><div class=""> private let a: A; private let b: B</div><div class=""> }</div><div class=""><br class=""></div><div class=""> struct ExtremaIterator<A:Iterator,B.Iterator where A.Element == B.Element, A.Element:Comparable> {</div><div class=""> private var a: A?</div><div class=""> private var b: B?</div><div class=""> </div><div class=""> mutating func next() -> (A.Element,A.Element)? {</div><div class=""> guard let nextA = a?.next() else {</div><div class=""> a = nil; b = nil; </div><div class=""> return nil</div><div class=""> }</div><div class=""> guard let nextB = b?.next() else {</div><div class=""> a = nil; b = nil; </div><div class=""> return nil</div><div class=""> }</div><div class=""> if nextA < nextB { </div><div class=""> return (nextA,nextB)</div><div class=""> } else {</div><div class=""> return (nextB,nextA)</div><div class=""> } </div><div class=""> }</div><div class=""><br class=""></div><div class=""> }</div><div class=""><br class=""></div><div class="">…would it be necessary to write the type’s generic parameters like this:</div><div class=""><br class=""></div><div class=""> struct VariadicExtremaSequence<E:Comparable,S… where S:Sequence, S.Iterator.Element == E></div><div class=""><br class=""></div><div class="">…or would there be some way of writing a constraint like “all `S.Iterator` must have same `.Element`?”?</div><div class=""><br class=""></div><div class="">A.2: Support for non-uniform "parameter-to-parameter” constraints</div><div class=""><br class=""></div><div class="">As an example:</div><div class=""><br class=""></div><div class=""> protocol ValueTransformer {</div><div class=""> associatedtype Input</div><div class=""> associatedtype Output</div><div class=""> </div><div class=""> func transformedValue(for input: Input) throws -> Output</div><div class=""><br class=""></div><div class=""> }</div><div class=""><br class=""></div><div class=""> // how to express this (if possible), mock syntax below</div><div class=""> struct LoggingValueTransformerApplier<</div><div class=""> T:ValueTransformer</div><div class=""> where</div><div class=""> …T[i],T[j]… => T[i].Output == T[j].Input> : ValueTransformer {</div><div class=""><br class=""></div><div class=""> typealias Input = #first(T…).Input</div><div class=""> typealias Output = #last(T…).Output</div><div class=""><br class=""></div><div class=""> private let (transformers) = (T...)</div><div class=""><br class=""></div><div class=""> func transformedValue(for input: Input) throws -> Output {</div><div class=""> // evaluate incrementally, logging input => (output | error) step-by-step</div><div class=""> }</div><div class=""><br class=""></div><div class="">}</div><div class=""><br class=""></div><div class="">…something like the above is definitely a "nice to have", but not having it will close off certain use cases.</div><div class=""><br class=""></div><div class="">B: Integer-Based Indexing</div><div class=""><br class=""></div><div class="">I see it’s been brought up already, but I’d *strongly* suggest making support for integer-based “indexing” into variadic packs a priority.</div><div class=""><br class=""></div><div class="">What concerns me is that without support for that, I suspect a lot of code would have to get "written twice” if using the “recursive” API on parameter packs.</div><div class=""><br class=""></div><div class="">Here’s a mock example:</div><div class=""> </div><div class=""> // a “weaker” dictionary-like protocol</div><div class=""> protocol LookupTable {</div><div class=""> associatedtype Key: Hashable</div><div class=""> associatedtype Value</div><div class=""><br class=""></div><div class=""> subscript(key: Key) -> Value? { get } </div><div class=""><br class=""></div><div class=""> }</div><div class=""><br class=""></div><div class=""> /// Does a cascading search for values through a possibly *heterogeneous*</div><div class=""> /// collection of backing lookup tables…caching results to avoid subsequent searches</div><div class=""> struct HeterogeneousCascadingLookupTable<K:Hashable,V,T… </div><div class=""> where </div><div class=""> T:LookupTable,</div><div class=""> T.Key == K,</div><div class=""> T.Value == V> : LookupTable {</div><div class=""><br class=""></div><div class=""> private let (tables): (T…)</div><div class=""> private var valueCache: [K:V] = [:]</div><div class=""> private var missingKeys: Set<K> = []</div><div class=""><br class=""></div><div class=""> // implementation *with* integer-based indexing: </div><div class=""> subscript(key: K) -> V? {</div><div class=""> get {</div><div class=""> guard !missingKeys.contains(key) else { return nil }</div><div class=""> if let cached = valueCache[key] { return cached }</div><div class=""> for index in 0..<#count(T…) {</div><div class=""> if let v = tables[index][key] {</div><div class=""> valueCache[key] = v</div><div class=""> return v</div><div class=""> }</div><div class=""> }</div><div class=""> missingKeys.insert(key)</div><div class=""> return nil</div><div class=""> }</div><div class=""> }</div><div class=""><br class=""></div><div class=""> // implementation without integer-based indexing (or equivalent mechanism):</div><div class=""><div class=""> subscript(key: K) -> V? {</div><div class=""> get {</div><div class=""> guard !missingKeys.contains(key) else { return nil }</div><div class=""> if let cached = valueCache[key] { return cached }</div><div class=""> if let value = self.lookupValue(for: key, in: self.tables) {</div><div class=""> valueCache[key] = value</div><div class=""> return value</div><div class=""> } else {</div><div class=""> missingKeys.insert(key)</div><div class=""> return nil</div><div class=""> }</div><div class=""> }</div><div class=""> }</div></div><div class=""><br class=""></div><div class=""> // recursive lookup implementation (necessary b/c our type itself</div><div class=""> // isn’t defined by recursively-nesting itself)</div><div class=""> private final func lookupValue<U… </div><div class=""> where </div><div class=""> U…: LookupTable,</div><div class=""> U.Key == K,</div><div class=""> U.Value == V>(for key: K, in tables: U…) -> V? {</div><div class=""> return #first(tables)[key] ?? self.lookupValue(for: key, in: #rest(tables))</div><div class=""> }</div><div class=""><br class=""></div><div class="">}</div><div class=""><br class=""></div><div class="">…which isn’t the end of the world, but having to write a separate recursive implementation of each method that needs to step-through the variadic parameters would spread things out a bit, it’d seem.</div><div class=""><br class=""></div><div class="">(Yes, this specific thing could be written today w/out variadics, but it illustrates the tradeoff here).</div><div class=""><br class=""></div><div class="">C: “Variadic-Sum” / Structural-Union?</div><div class=""><br class=""></div><div class="">I mentioned this before in a response to the manifesto email, but will reiterate it here: there’s a fair amount of useful things you could do with variadic generics *if* there was also some similar way to get a “structural union”.</div><div class=""><br class=""></div><div class="">A simple motivating example is something like a `Chain` sequence (analogous to `Zip`); e.g. something for which `chain(a,b)` is the sequence of ‘the elements in `a`, then the elements in `b`.</div><div class=""><br class=""></div><div class="">Although the proposed variadics make the sequence itself easy to write:</div><div class=""><br class=""></div><div class=""> // for now I'd assume we need the `E` as a separate type parameter here,</div><div class=""> // but recall the previous point</div><div class=""> struct ChainSequence<E,S… where S:Sequence, S.Iterator.Element == E> {</div><div class=""> private let (…sequences): (S…)</div><div class=""> init(…arguments: S…) { … } </div><div class=""> }</div><div class=""><br class=""></div><div class="">…the iterator is a bit more complicated. Without “structural unions” you’d probably wind up with something like this for a variadic implementation:</div><div class=""><br class=""></div><div class=""> struct ChainSequenceIterator<E,I… where I:Iterator, I.Element == E> {</div><div class=""><br class=""></div><div class=""> private var (…iterators): (I?…) // <- I hope this works</div><div class=""><br class=""></div><div class=""> mutating func next() -> E? {</div><div class=""> // find first non-nil iterator, try to get next element from it; </div><div class=""> // if next is non-nil, return that, otherwise nil it out and</div><div class=""> // find the *next* non-nil iterator, etc….</div><div class=""> }</div><div class=""><br class=""></div><div class=""> }</div><div class=""><br class=""></div><div class="">…which could be made to work, but is wasteful in some ways (you have space to store n iterators…). You can make it a little cleaner if you have some way of doing integer-based indexing into the variadic parameters, but that doesn’t change the space requirements (it’s still going to need room for all `n` iterators + bookkeeping).</div><div class=""><br class=""></div><div class="">If, however, you have some variadic-compatible structural unions, you can write it as (roughly):</div><div class=""><br class=""></div><div class=""><div class=""> struct ChainSequenceIterator<E,S… where S:Sequence, S.Iterator.Element == E> {</div></div><div class=""> private let source: ChainSequence<E,S…></div><div class=""> private var iterator: (S.Iterator |…) // (meant to be ~ `(S1.Iterator | S2.Iterator | S3.Iterator | …. | SN.Iterator)` )</div><div class=""> private var done: Bool = false</div><div class=""><br class=""></div><div class=""> mutating func next() -> E? {</div><div class=""> // if not `done`, try `next` on current iterator in `iterator`</div><div class=""> // if non-nil, return, otherwise advance to next iterator, etc., ...</div><div class=""> }</div><div class=""> }</div><div class=""><br class=""></div><div class="">…(how much space this actually saves depends on the size of `source` vs the size of the iterators, but it’s *possible* to save space this way). </div><div class=""><br class=""></div><div class="">Variadic generics *can* be added w/out structural unions — they’re a pure "nice-to-have" — but having support for variadic "product types" w/out corresponding variadic "sum types" is going to complicate-or-prevent certain constructs.</div><div class=""><br class=""></div><div class="">D: Make Parameter-Packs Named</div><div class=""><br class=""></div><div class="">I understand the appeal of the `…T`-style syntax, but I’d at least consider giving parameter-packs a more-concrete existence, e.g. something like this:</div><div class=""><br class=""></div><div class=""> // strawman declaration syntax:</div><div class=""> struct Foo<I,J,K#ParameterPack> {</div><div class=""><br class=""></div><div class=""> // `K` here will refer to the variadic parameter-pack itself;</div><div class=""> // cf the `typealias` below , but basically K ~ the tuple of its types</div><div class=""><br class=""></div><div class=""> typealias KValueTuple = #tuple(K) // == tuple of values of type K.0, K.1, etc.)</div><div class=""> typealias KTypeTyple - #typeTuple(K) // == tuple of types like K.0, K.1</div><div class=""> typealias FirstK = #first(K) </div><div class=""> typealias LastK = #last(K) </div><div class=""> static var kArity: Int { return #count(K) } </div><div class=""> // and so on</div><div class=""><br class=""></div><div class=""> }</div><div class=""><br class=""></div><div class=""><div class=""> // straw man point-of-use syntax, can be adjusted of course:</div></div><div class=""> let concreteFoo = Foo<I,J,K:(K0,K1,K2,…,Kn)></div><div class=""><br class=""></div><div class="">…which complicates the grammar, but IMHO feels a lot nicer than having a lot of "implicit rules" about what `…` means and so on.</div><div class=""><br class=""></div><div><blockquote type="cite" class=""><div class="">On May 28, 2016, at 3:03 PM, Austin Zheng via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" class="">Hello swift-evolution,<div class=""><br class=""></div><div class="">I put together a draft proposal for the variadic generics feature described in "Completing Generics" as a major objective for Swift 3.x. It can be found here:</div><div class=""><br class=""></div><div class=""><a href="https://github.com/austinzheng/swift-evolution/blob/az-variadic-generics/proposals/XXXX-variadic-generics.md" class="">https://github.com/austinzheng/swift-evolution/blob/az-variadic-generics/proposals/XXXX-variadic-generics.md</a><br class=""></div><div class=""><br class=""></div><div class="">It adopts the syntax and semantics that are described in Completing Generics, and attempts to remain as simple as possible while still being useful for certain use cases (although I think there is still room to simplify). The proposal contains sample implementations for four use cases:</div><div class=""><br class=""></div><div class="">- Arbitrary-arity 'zip' sequence</div><div class="">- Arbitrary-arity tuple comparison for equality</div><div class="">- Tuple splat/function application</div><div class="">- Multiple-arity Clojure-style 'map' function</div><div class=""><br class=""></div><div class="">There is a lot of scope for design refinements, and even for alternative designs. With enhanced existentials, there was already an informal consensus that the feature would involve composing some protocols and class requirements, and placing constraints on the associated types, and most everything else was working out the various implications of doing so. That's not true for this feature.</div><div class=""><br class=""></div><div class="">In particular, I'm interested to see if there are similarly expressive designs that use exclusively tuple-based patterns and no parameter packs. I think Rust once considered a similar approach, although their proposal ended up introducing a parameter-pack like construct for use with fn application: <a href="https://github.com/rust-lang/rfcs/issues/376" class="">https://github.com/rust-lang/rfcs/issues/376</a></div><div class=""><br class=""></div><div class="">Feedback would be greatly appreciated. Again, be unsparing.</div><div class=""><br class=""></div><div class="">Best,</div><div class="">Austin</div><div class=""><br class=""></div></div>
_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></div></blockquote></div><br class=""></body></html>