<div dir="ltr"><div>I&#39;ve moved this discussion over from &quot;[swift-evolution] [Proposal] mapValues&quot; which discusses mapValues, like map but it preserves keys and transforms values, returning a Dictionary.</div><div><br></div><div>I&#39;m in favour mapValues, but I feel like it should be the default behaviour of map for a Dictionary. Being the default requires changes to protocols (many already planned), language features (already planned), and addressing many interface inconsistencies between Array and Dictionary.</div><div><br></div><div>---- continuing from the other thread ----</div><div><br></div><div>Thanks Brent for the in depth analysis!<br></div><div><br></div><div>As I mentioned at the top: I was focusing on the usage examples not the implementation. I&#39;ve made compromises in this implementation to conform to the existing protocols. <font size="2"><span style="background-color:rgba(255,255,255,0)">Many of these compromises would probably be addressed by SE0065 (<a href="https://github.com/apple/swift-evolution/blob/master/proposals/0065-collections-move-indices.md" target="_blank">https://github.com/apple/swift-evolution/blob/master/proposals/0065-collections-move-indices.md</a>).</span></font><div><br></div><div>Answers inline.</div><div><br>On Saturday, 16 April 2016, Brent Royal-Gordon &lt;<a>brent@architechies.com</a>&gt; wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">&gt; This is a clarification on what I meant (I haven&#39;t had much time to test it, but I think it&#39;s representative):<br>&gt;<br>&gt; <a href="https://gist.github.com/therealbnut/c223d90a34bb14448b65fc6cc0ec70ac" target="_blank">https://gist.github.com/therealbnut/c223d90a34bb14448b65fc6cc0ec70ac</a><br><br>There are a number of problems with this:<br><br>* Given just an `Index`, you need to be able to calculate the next `Index`, and check if it lies before the `endIndex`. The complexity of this is hidden in your example because you&#39;re relying on a traditional `DictionaryIndex` to perform these tasks. Thus, the `Index` type in your dictionary design would be some sort of wrapper-around-the-key, not the key itself. That&#39;s the source of the `subscript(_: Index) -&gt; Value` which apparently confused you.<br><br></blockquote><div>This was an implementation detail, see the comment at the top of the file. <font size="2"><span style="background-color:rgba(255,255,255,0)">If SE0065 is implemented then the previous/next/lastIndex can be found from the collection in constant amortized time.</span></font> This is possible after SE0065.</div><div><font size="2"><span style="background-color:rgba(255,255,255,0)"><br></span></font></div><div><font size="2"><span style="background-color:rgba(255,255,255,0)">I wasn&#39;t confused, but I was unsure of the best approach. Array and Dictionary probably need different Indexable-style protocols.</span></font></div><div><font size="2"><span style="background-color:rgba(255,255,255,0)"><br></span></font></div><div><font size="2"><span style="background-color:rgba(255,255,255,0)">The main issue is that Dictionary can get/set an optional Value to insert/remove values. </span></font><span style="background-color:rgba(255,255,255,0)">Array doesn&#39;t currently have Optional get/set. Being Optional doesn&#39;t make sense for an Array unless it&#39;s sparse. It would be unexpected if an Array&#39;s `.endIndex != .count`.</span></div><div><span style="background-color:rgba(255,255,255,0)"><br></span></div><div><font size="2"><span style="background-color:rgba(255,255,255,0)">I also think that, ideally, `Index == Key` for a Dictionary.</span></font></div><div><font size="2"><span style="background-color:rgba(255,255,255,0)"><br></span></font></div><div><font size="2"><span style="background-color:rgba(255,255,255,0)">There may be an index-like type which allows lookup with a lower constant overhead (bucket index), but it&#39;s likely to only be used by extensions, if at all.</span></font></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">* Generic `SequenceType` and `CollectionType` operations now use only the values, not the keys and values. That means that `Array(newDictionary)` gets you an array of values, `filter` returns an array of values, `reduce` operates only on the values, etc; all of these operations throw away the keys. That&#39;s not necessarily wrong, but I&#39;m not sure that you&#39;re aware of it. </blockquote><div> </div><div>I&#39;m fine for `Array(newDictionary)` to discard keys, there&#39;s always `Array(newDictionary.enumerate())`. I think it&#39;s more consistent for filter to work on values. I think all collections would also benefit from versions of map/filter/reduce etc. that also have an Index (so you could get every second value of an array, for example).</div><div><br></div><div>I&#39;d like the protocol to have associatedtype FilterType, for NewDictionary it would be defined as something like:</div><div><font face="monospace, monospace">    typealias FilterType = NewDictionary&lt;Key,Value&gt;</font></div><div>This would allow filter, reduce, etc. to preserve the keys.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">* Your `map` is overloading `SequenceType.map` merely by return type. That means you can no longer assign it directly to a newly-declared variable, or use it in many other contexts where the type has to be inferred.</blockquote><div><br></div><div>Overloading is an implementation detail at the moment, as mentioned in the comment at the top of the file. Ideally this would be <font size="2"><span style="background-color:rgba(255,255,255,0)">Self.MapType once we have generic associatedtypes</span></font>. For dictionaries:</div><div><span style="font-family:monospace,monospace">    typealias MapType&lt;T&gt; = NewDictionary&lt;Key,T&gt;</span><br></div><div><span style="font-family:monospace,monospace"><br></span></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><br>* Your `enumerate` is similarly overloading by return type. It&#39;s also further confusing an already confused semantic. `enumerate` does not pair elements with their indices; it pairs them with integers starting at zero. This *happens* to correspond to their array indices, but that&#39;s not necessarily true of any other Collection. (I&#39;m going to start another thread about this.)<br><br></blockquote><div><font size="2"><span style="background-color:rgba(255,255,255,0)">The overloaded return type is an unfortunate implementation detail, to fit the existing protocols. The existing enumerate uses (Int,Value), as you mentioned, i&#39;m not sure if enumerate is changing after SE0065. Changing it to (Index,Value) would solve the overload issue. </span></font>I&#39;ve seen a lot of array code that uses enumerate() this way currently. I&#39;m not sure what enumerate is meant to be for if (Index,Value) is inappropriate.</div><div><font size="2"><span style="background-color:rgba(255,255,255,0)"><br></span></font></div><div><font size="2"><span style="background-color:rgba(255,255,255,0)">Either way I think all collections would benefit from a property that exposes a sequence of (Index,Value) pairs.</span></font></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">Basically, in this conception of Dictionary:<br><br>* The keys are disposable and the values are the important part—most operations on a Dictionary would throw away the keys and use only the values.</blockquote><div>Agreed, this may add complexity to generic/default implementations, or require changes to some of the protocols (my preference is protocol changes). </div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">* Index still would not be a Key—it would be some kind of instance which wrapped or converted into a Key.</blockquote><div>After SE0065 they can be the same.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">* You still would need to name Dictionary-creating `map` and similar functions differently from their Array-generating cousins.</blockquote><div> </div><div>Only if you want map to return an array. There&#39;s a good argument for Self.MapType etc. once we have generic associatedtype.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">* Iterating over keys and values together would require some kind of Dictionary-specific API, not something that was available on other collections or sequences.</blockquote><div><br></div><div>I think that the existing `enumerate()` should do, if it is changed to return (Index,Value) rather than (Int,Value). The downside is that dictionaries would require `.enumerate()` added to many for loops, I agree that this is not ideal. It may be that I&#39;m misinterpreting enumerate, there could probably be a shorter property name that would suffice.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><br>Maybe that would be an improvement, but I&#39;m skeptical.<br><br></blockquote><div>Skeptical is good, it wasn&#39;t perfect, as long you have an open mind :)</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">--<br>Brent Royal-Gordon<br>Architechies<br></blockquote></div></div></div>