<div dir="ltr">+1<br></div><br><div class="gmail_quote"><div dir="ltr">On Wed, 12 Oct 2016 at 09:09 Said Sikira via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>&gt; wrote:<br></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" class="gmail_msg"><div id="m_-8426030209915870348bloop_customfont" style="font-family:Helvetica,Arial;font-size:13px;color:rgba(0,0,0,1.0);margin:0px;line-height:auto" class="gmail_msg">+1</div></div><div style="word-wrap:break-word" class="gmail_msg"> <br class="gmail_msg"> <div id="m_-8426030209915870348bloop_sign_1476256035667259904" class="m_-8426030209915870348bloop_sign gmail_msg"></div> <br class="gmail_msg"><p class="m_-8426030209915870348airmail_on gmail_msg">On October 12, 2016 at 8:54:45 AM, Rien via swift-evolution (<a href="mailto:swift-evolution@swift.org" class="gmail_msg" target="_blank">swift-evolution@swift.org</a>) wrote:</p> <blockquote type="cite" class="m_-8426030209915870348clean_bq gmail_msg"><span class="gmail_msg"><div class="gmail_msg"><div class="gmail_msg"></div><div class="gmail_msg">Beautiful, +1<br class="gmail_msg"><br class="gmail_msg">Rien<br class="gmail_msg"><br class="gmail_msg">&gt; On 11 Oct 2016, at 23:28, Nate Cook via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="gmail_msg" target="_blank">swift-evolution@swift.org</a>&gt; wrote:<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; Introduction<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; This proposal addresses significant unexpected performance gaps when using dictionaries. It introduces type-specific collections for a Dictionary instance&#39;s keys and values properties.<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; New DictionaryKeys and DictionaryValues collections provide efficient key lookup and mutable access to dictionary values, enabling updates to be performed in-place and allowing copy-on-write optimization of stored values.<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; Motivation<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; This proposal address two problems:<br class="gmail_msg">&gt; <br class="gmail_msg">&gt;         • The Dictionary type keys implementation is inefficient, because LazyMapCollection doesn&#39;t know how to forward lookups to the underlying dictionary storage.<br class="gmail_msg">&gt;         • Dictionaries do not offer value-mutating APIs. The mutating key-based subscript wraps values in an Optional. This prevents types with copy-on-write optimizations from recognizing they are singly referenced.<br class="gmail_msg">&gt; This proposal uses the following [String: [Int]] dictionary to demonstrate these problems:<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; var dict = [&quot;one&quot;: [1], &quot;two&quot;: [2, 2], &quot;three&quot;: [3, 3, 3]]<br class="gmail_msg">&gt; Inefficient dict.keys Search<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; Swift coders normally test key membership using nil checks or underscored optional bindings:<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; if dict[&quot;one&quot;] != nil<br class="gmail_msg">&gt;  {<br class="gmail_msg">&gt;     <br class="gmail_msg">&gt; // ...<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; }<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; if let _ = dict[&quot;one&quot;<br class="gmail_msg">&gt; ] {<br class="gmail_msg">&gt;     <br class="gmail_msg">&gt; // ...<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; }<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; These approaches provide the expected performance of a dictionary lookup but they read neither well nor &quot;Swifty&quot;. Checking keys reads much better but introduces a serious performance penalty: this approach requires a linear search through a dictionary&#39;s keys to find a match.<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; if dict.keys.contains(&quot;one&quot;) {<br class="gmail_msg">&gt;     // ...<br class="gmail_msg">&gt; }<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; A similar dynamic plays out when comparing dict.index(forKey:) and dict.keys.index(of:).<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; Inefficient Value Mutation<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; Dictionary values can be modified through the keyed subscript by direct reassignment or by using optional chaining. Both of these statements append 1 to the array stored by the key &quot;one&quot;:<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; // Direct re-assignment<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; dict[<br class="gmail_msg">&gt; &quot;one&quot;] = (dict[&quot;one&quot;] ?? []) + [1<br class="gmail_msg">&gt; ]<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; <br class="gmail_msg">&gt; // Optional chaining<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; dict[<br class="gmail_msg">&gt; &quot;one&quot;]?.append(1)<br class="gmail_msg">&gt; Both approaches present problems. The first is complex and hard to read. The second ignores the case where &quot;one&quot; is not a key in the dictionary. It forces its check into a higher branch and encourages forced unwrapping. Furthermore, neither approach allows the array to grow in place. They introduce an unnecessary copy of the array&#39;s contents even though dict is the sole holder of its storage.<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; Adding mutation to a dictionary&#39;s index-based subscripting isn&#39;t possible. Changing a key stored at a particular index would almost certainly modify its hash value, rendering the index incorrect. This violates the requirements of the MutableCollection protocol.<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; Proposed Solution<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; This proposal adds a custom collection for the keys and values dictionary properties. This follows the example set by String, which presents multiple views of its contents. A new DictionaryKeys collection introduces efficient key lookup, while a new DictionaryValues collection provides a mutable collection interface to dictionary values.<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; These changes introduce a simple and efficient way of checking whether a dictionary includes a key:<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; // Performant<br class="gmail_msg">&gt; if dict.keys.contains(&quot;one&quot;<br class="gmail_msg">&gt; ) {<br class="gmail_msg">&gt;     <br class="gmail_msg">&gt; // ...<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; }<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; As a mutable collection, values enables modification without copies or clumsy code:<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; if let i = dict.index(forKey: &quot;one&quot;<br class="gmail_msg">&gt; ) {<br class="gmail_msg">&gt;     dict<br class="gmail_msg">&gt; .values[i].append(1)  // no copy here<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; } <br class="gmail_msg">&gt; else<br class="gmail_msg">&gt;  {<br class="gmail_msg">&gt;     dict[<br class="gmail_msg">&gt; &quot;one&quot;] = [1<br class="gmail_msg">&gt; ]<br class="gmail_msg">&gt; }<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; Both the keys and values collections share the same index type as Dictionary. This allows the above sample to be rewritten as:<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; // Using `dict.keys.index(of:)`<br class="gmail_msg">&gt; if let i = dict.keys.index(of: &quot;one&quot;<br class="gmail_msg">&gt; ) {<br class="gmail_msg">&gt;     dict<br class="gmail_msg">&gt; .values[i].append(1<br class="gmail_msg">&gt; )<br class="gmail_msg">&gt; } <br class="gmail_msg">&gt; else<br class="gmail_msg">&gt;  {<br class="gmail_msg">&gt;     dict[<br class="gmail_msg">&gt; &quot;one&quot;] = [1<br class="gmail_msg">&gt; ]<br class="gmail_msg">&gt; }<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; Detailed design<br class="gmail_msg">&gt; <br class="gmail_msg">&gt;         • The standard library introduces two new collection types: DictionaryKeys and DictionaryValues.<br class="gmail_msg">&gt;         • A Dictionary&#39;s keys and values property types change from LazyMapCollection to these new types. <br class="gmail_msg">&gt;         • The new collection types are not directly constructable. They are presented only as views into a dictionary.<br class="gmail_msg">&gt; struct Dictionary&lt;Key: Hashable, Value&gt;: ...<br class="gmail_msg">&gt;  {<br class="gmail_msg">&gt;     <br class="gmail_msg">&gt; var keys: DictionaryKeys&lt;Key, Value&gt; { get<br class="gmail_msg">&gt;  }<br class="gmail_msg">&gt;     <br class="gmail_msg">&gt; var values: DictionaryValues&lt;Key, Value&gt; { get set<br class="gmail_msg">&gt;  }<br class="gmail_msg">&gt; <br class="gmail_msg">&gt;     <br class="gmail_msg">&gt; // Remaining declarations<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; }<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; <br class="gmail_msg">&gt; /// A collection view of a dictionary&#39;s keys.<br class="gmail_msg">&gt; struct DictionaryKeys&lt;Key: Hashable, Value&gt;<br class="gmail_msg">&gt; : Collection {<br class="gmail_msg">&gt;     <br class="gmail_msg">&gt; typealias Index = DictionaryIndex&lt;Key, Value&gt;<br class="gmail_msg">&gt; <br class="gmail_msg">&gt;     <br class="gmail_msg">&gt; subscript(i: Index) -&gt; Key { get<br class="gmail_msg">&gt;  }<br class="gmail_msg">&gt; <br class="gmail_msg">&gt;     <br class="gmail_msg">&gt; // Other `Collection` requirements<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; }<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; <br class="gmail_msg">&gt; /// A mutable collection view of a dictionary&#39;s values.<br class="gmail_msg">&gt; struct DictionaryValues&lt;Key: Hashable, Value&gt;<br class="gmail_msg">&gt; : MutableCollection {<br class="gmail_msg">&gt;     <br class="gmail_msg">&gt; typealias Index = DictionaryIndex&lt;Key, Value&gt;<br class="gmail_msg">&gt; <br class="gmail_msg">&gt;     <br class="gmail_msg">&gt; subscript(i: Index) -&gt; Value { get set<br class="gmail_msg">&gt;  }<br class="gmail_msg">&gt; <br class="gmail_msg">&gt;     <br class="gmail_msg">&gt; // Other `Collection` requirements<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; }<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; A sample implementation of this proposal can be found in this branch.<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; Impact on existing code<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; The performance improvements of using the new DictionaryKeys type and the mutability of the DictionaryValuescollection are both additive in nature.<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; Most uses of these properties are transitory in nature. Adopting this proposal should not produce a major impact on existing code. The only impact on existing code exists where a program explicitly specifies the type of a dictionary&#39;s keysor values property. The fix is to change the specified type. <br class="gmail_msg">&gt; <br class="gmail_msg">&gt; Alternatives considered<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; The Generics Manifesto lists nested generics as a goal. This could impact the naming and structure of these new collection types. <br class="gmail_msg">&gt; <br class="gmail_msg">&gt; Instead of DictionaryKeys&lt;Key, Value&gt; and DictionaryValues&lt;Key, Value&gt;, these types could be Dictionary&lt;Key, Value&gt;.Keys and Dictionary&lt;Key, Value&gt;.Values. However, because many types in the standard library may be revisited once such a feature is available (indices, iterators, etc.), the current lack of nesting shouldn&#39;t prevent consideration of this proposal.<br class="gmail_msg">&gt; <br class="gmail_msg">&gt; It could be possible to add additional compiler features that manage mutation through existing key-based subscripting without the copy-on-write problems of the current implementation. I don&#39;t know enough about how that would be implemented to speak to its feasibility or level of effort. Such a feature would reduce the need for a mutable DictionaryValues collection.<br class="gmail_msg">&gt; _______________________________________________<br class="gmail_msg">&gt; swift-evolution mailing list<br class="gmail_msg">&gt; <a href="mailto:swift-evolution@swift.org" class="gmail_msg" target="_blank">swift-evolution@swift.org</a><br class="gmail_msg">&gt; <a href="https://lists.swift.org/mailman/listinfo/swift-evolution" class="gmail_msg" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br class="gmail_msg"><br class="gmail_msg">_______________________________________________<br class="gmail_msg">swift-evolution mailing list<br class="gmail_msg"><a href="mailto:swift-evolution@swift.org" class="gmail_msg" target="_blank">swift-evolution@swift.org</a><br class="gmail_msg"><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" class="gmail_msg" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br class="gmail_msg"></div></div></span></blockquote></div>
_______________________________________________<br class="gmail_msg">
swift-evolution mailing list<br class="gmail_msg">
<a href="mailto:swift-evolution@swift.org" class="gmail_msg" target="_blank">swift-evolution@swift.org</a><br class="gmail_msg">
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" class="gmail_msg" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br class="gmail_msg">
</blockquote></div>