<div dir="ltr">(inline, again)<br><div class="gmail_extra"><br><div class="gmail_quote">On Mon, May 30, 2016 at 5:44 AM, plx via swift-evolution <span dir="ltr">&lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word"><div>I’ll keep this quick since I see upthread you have already revised the proposal.</div><br><div><span class=""><blockquote type="cite"><div>On May 29, 2016, at 12:36 PM, Austin Zheng &lt;<a href="mailto:austinzheng@gmail.com" target="_blank">austinzheng@gmail.com</a>&gt; wrote:</div><br><div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><blockquote type="cite"><div><div style="word-wrap:break-word"><div>…something like the above is definitely a &quot;nice to have&quot;, but not having it will close off certain use cases.</div></div></div></blockquote><div><br></div><div>Oh, this is interesting. So a way to specify an equality relationship between &quot;adjacent&quot; sets of associated types?</div></div></div></blockquote><div><br></div></span><div>Right, that was what I had in mind, and I can think of uses for such neighbor-to-neighbor type relationships.</div><div><br></div><div>There may be uses for other relationships—both beyond equality and beyond neighbor-to-neighbor—but I can’t think of any offhand.</div><div><br></div><div>One other pack-level constraint came to mind:</div><div><br></div><div>- can you enforce (T…) is *exactly* (T,T,T,…) N times? (e.g. T0 == T1, etc., not just &quot;conforms to same protocols” or “have identical associated types”)</div></div></div></blockquote><div><br></div><div>Yes, this makes sense. #allequal(T...), maybe?</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word"><div><div><br></div><div>…along with some “pack-to-pack” constraints:</div><div><br></div><div>- if you have e.g. 2+ packs, can you enforce (T…) and (U…) have the same arity?</div><div>- if you have e.g. 2+ packs, could you enforce e.g. T.Foo… == U.Bar ?</div></div></div></blockquote><div><br></div><div>Yes, an explicit &quot;same length&quot; constraint makes sense. Otherwise, if a pack is derived from another pack they are implied to have the same length.</div><div><br></div><div>I think the second constraint should be possible already? I&#39;ll double-check.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word"><div><div><br></div><div>…as always nice-to-have, but throwing them out there for consideration.</div><span class=""><div><br></div><blockquote type="cite"><div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><div>I&#39;ll write something up.</div><div><br></div><div><div>for index in 0..&lt;#count(T…) {</div><div>    if let v = tables[index][key] {</div><div>        valueCache[key] = v</div><div>   <span> </span><span style="white-space:pre-wrap">        </span>return v</div><div>    }</div><div>}</div><div><br></div><div>I assume this is all compile time code generation (unroll the loop in the source #count(T...) times for each arity that T... is instantiated as, with a different &#39;tables&#39; pack member value for each unrolled loop iteration). </div></div></div></div></blockquote><div><br></div></span><div>I’d assume so too, but thinking it through I think it needs some alternative way of being expressed.</div><div><br></div><div>If you look @ the “obvious” implementation for the above as written, it unrolls into something like this:</div><div><br></div><div>  if let v = tables[0][key] { … } // assume returns out of here on success</div><div>  if let v = tables[1][key] { … } // assume returns out of here on success</div><div>  //…etc...</div><div><br></div><div>…but b/c tuples aren’t directly subscriptable, those `tables[index][key]` expressions themselves would perhaps get expanded into the equivalent of, e.g.:</div><div> </div><div>  private func __subscriptTables(index index: Int, key: K) -&gt; V? {</div><div>    switch index {</div><div>       case 0: return tables.0[key]</div><div>       case 1: return tables.1[key]</div><div>       // etc...</div><div>       default: fatalError(“Invalid subscript into variadic…blah blah”)</div><div>    }</div><div>  }</div><div><br></div><div>…and so the original expansion would be more like:</div><div><br></div><div><div>  if let v = __subscriptTables(index:0, key: key) { … } // assume returns out of here on success</div><div>  if let v = __subscriptTables(index:1, key: key) { … } // assume returns out of here on success</div><div>  //…etc...</div><div><br></div><div>….which repeats the switch at each line. In theory the optimizer can know to inline `__subscriptTables`, notice `index` is known at compile-time, replace the switch with direct access, and arrive at the code you “really want”:</div><div><br></div><div><div>  if let v = tables.0[key] { … } // assume returns out of here on success</div><div>  if let v = tables.1[key] { … } // assume returns out of here on success</div></div><div><br></div><div>…but that’s obviously putting a lot of pressure on the compiler to convert the `for index in #count(tables) { … }` code into something equivalent-but-reasonable.</div><div><br></div><div>I’ll be sure to look @ at the proposed `fold` in this light.</div></div></div></div></blockquote><div><br></div><div>I&#39;m not sure if &#39;fold&#39; as currently expressed is powerful enough to do something like this. I suppose a variant of fold that took its parameter &#39;inout&#39; would work, especially if your &#39;fold&#39; type was a (isFinished, desiredValue) tuple (or maybe even just &quot;desiredValue?&quot; as an optional).</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word"><div><div><div><br></div></div><span class=""><blockquote type="cite"><div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><div>This is interesting. Might it be possible with to accomplish this with existentials (this is sort of a cross-reference to a different proposal from the generics manifest)? An existential type as described below would work for any pack where all the elements were constrained in the same way. Not sure if it could be made to work in the case where the types in the pack are related to each other (as proposed earlier).</div><div><br></div><div>struct ChainSequenceIterator&lt;E, S… where S:Sequence, S.Iterator.Element == E&gt; {</div><div><br></div><div><span style="white-space:pre-wrap">        </span>// A single variable that contains each iterator in turn; specific type doesn&#39;t matter as long as the element is E</div><div><span style="white-space:pre-wrap">        </span>private var iterator : Any&lt;Iterator where .Element == E&gt;?</div><div><br></div><div><span style="white-space:pre-wrap">        </span>// ...</div><div>} </div></div></div></blockquote><div><br></div></span>Actually yes, I hadn’t thought of that and you could make it work in this case (although perhaps with some indirection overhead? and it seems also with some additional state tracking to know which iterator you actually have).</div><div><br></div><div>Where I’m not as sure is for something like a `ChainCollectionIndex` (same `stuff from A, then stuff from B, etc” concept, but for A, B collections, and so on).</div><div><br></div><div>That’s more clearly a case where what you *want* ideally is something like this:</div><div><br></div><div>  struct ChainCollection2&lt;A:Collection,B:Collection&gt; {</div><div>    let a: A; let b: B </div><div>  }</div><div><br></div><div>  struct ChainCollectionIndex2&lt;A:Comparable,B:Comparable&gt; {</div><div>    private var sourceIndex: Sum2&lt;A,B&gt; // e.g. (A | B)</div><div>  }</div><div><br></div><div>…since to implement the APIs you need A and A.Index and B and B.Index to &quot;match up&quot;. It’s probably possible here to do something like this instead:</div><div><br><div><div>  struct ChainCollectionIndex2&lt;A:Comparable,B:Comparable&gt; {</div><div>    private var boxedIndex: Box&lt;Any&gt; // actually either Box&lt;A&gt; or Box&lt;B&gt;</div><div>    private var whichIndex: AOrB // (E.G. enum ilke IndexFromA | IndexFromB)</div><div>  }</div><div><br></div><div>…with a lot of casting and so on, and perhaps also with existentials (and/or casting and open-as), but it’d be *cleaner* with the sum, still.</div><div><br></div><div>Either way you’re right that e.g. existentials and similar + state flags can cover a lot of these uses.</div></div></div></div></blockquote><div><br></div><div>I think union types (the A | B | C anonymous type commonly requested) would naturally complement this sort of functionality. You could have something like #union(T...), which is a scalar value whose type is T1 | T2 | ... | Tn.</div><div><br></div><div>Like you said, a lot of things are possible with even the simple model, when dynamic casting is taken into account. It&#39;s just more difficult to read and possibly slower to execute.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word"><div><div><div><br></div></div><span class=""><blockquote type="cite"><div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br><blockquote type="cite"><div><div style="word-wrap:break-word"><div><br></div><div>D: Make Parameter-Packs Named</div><div><br></div><div>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><br></div><div>  // strawman declaration syntax:</div><div>  struct Foo&lt;I,J,K#ParameterPack&gt; {</div><div><br></div><div>    // `K` here will refer to the variadic parameter-pack itself;</div><div>    // cf the `typealias` below , but basically K ~ the tuple of its types</div><div><br></div><div>    typealias KValueTuple = #tuple(K) // == tuple of values of type K.0, K.1, etc.)</div><div>    typealias KTypeTyple - #typeTuple(K) // == tuple of types like K.0, K.1</div><div>    typealias FirstK = #first(K) </div><div>    typealias LastK = #last(K) </div><div>    static var kArity: Int { return #count(K) } </div><div>    // and so on</div><div><br></div><div>  }</div><div><br></div><div><div>  // straw man point-of-use syntax, can be adjusted of course:</div></div><div>  let concreteFoo = Foo&lt;I,J,K:(K0,K1,K2,…,Kn)&gt;</div><div><br></div><div>…which complicates the grammar, but IMHO feels a lot nicer than having a lot of &quot;implicit rules&quot; about what `…` means and so on.</div></div></div></blockquote><div><br></div><div>I think it makes sense to make pack usage explicit. I think the dots at site of declaration don&#39;t really cause trouble, though, and are a little nicer to read than T#ParameterPack.</div></div></div></blockquote><div><br></div></span><div>I agree #ParameterPack is awful and that T… at the declaration site is not a big deal.</div><div><br></div><div>What I don’t like is not having a way to refer to the pack itself other than via its “placeholder type” with some dots.</div><div><br></div><div>It also seems nicer to express things like a pack fusion with names for packs, but again in fairness it’s not terrible:</div><div><br></div><div>  struct Foo&lt;T…&gt; {</div><div>  }</div><div><br></div><div>  func +++&lt;T…,U…&gt;(lhs: Foo&lt;T…&gt;, rhs: Foo&lt;U…&gt;) -&gt; Foo&lt;#fuse(T…,U…)&gt; </div><div><br></div><div>…so I’m not sure what I think on this.</div></div></div></blockquote><div><br></div><div>I&#39;ll keep on thinking about this.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word"><div><span class=""><br><blockquote type="cite"><div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br><blockquote type="cite"><div><div style="word-wrap:break-word"><div><br></div><div><blockquote type="cite"><div>On May 28, 2016, at 3:03 PM, Austin Zheng via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt; wrote:</div><br><div><div dir="ltr">Hello swift-evolution,<div><br></div></div></div></blockquote></div><br></div>_______________________________________________<br>swift-evolution mailing list<br><a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a><br><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a></div></blockquote></div></div></blockquote></span></div><br></div><br>_______________________________________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br>
<br></blockquote></div><br></div></div>