<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="">I think the points already-made about the weak semantics of such a type are well-founded and should curtail the idea of changing `Set` itself to take in optional “customized” functions for equality/hash.</div><div class=""><br class=""></div><div class="">However, this scenario, I think, makes an *excellent* testbed for Swift 3’s hopefully-still-incoming "conditional conformance” functionality (e.g. the thing that would let you say `Array:Equatable where Element:Equatable`).</div><div class=""><br class=""></div><div class="">Within Swift as it is right now, if you really want a “customizable set” as-requested, about the best you can do at this time is like so:</div><div class=""><br class=""></div><div class="">// step 1: a convenience protocol</div><div class="">protocol SetValueWrapper : Equatable, Hashable {</div><div class="">&nbsp; typealias Value // note: it’s useful to require `:Equatable`, but not necessary</div><div class="">&nbsp; var value: Value { get }</div><div class="">&nbsp; init(_ value: Value)</div><div class="">}</div><div class=""><br class=""></div><div class="">// step 2: a convenience wrapper around `Set`</div><div class="">//&nbsp;</div><div class="">// re-implement as much of the `Set` APIs as you need,</div><div class="">// but in a way that lets you ignore internal use of `W`</div><div class="">//&nbsp;</div><div class="">// Note that in practice you may need to write this as</div><div class="">// struct WrappedValueSet&lt;V,W:SetValueWrapper where V==W.Value&gt;,</div><div class="">// as I’ve run into bugs where the compiler needs that `V` to figure things out.</div><div class="">// I’ve written it as it should be, not how it may need to be to use today.</div><div class="">struct WrappedValueSet&lt;W:SetValueWrapper&gt; {</div><div class="">&nbsp; &nbsp;private var storage: Set&lt;W&gt; &nbsp;</div><div class=""><br class=""></div><div class="">&nbsp; // example re-implementations:</div><div class="">&nbsp; //</div><div class="">&nbsp; func contains(element: W.Element) -&gt; Bool { return self.storage.contains(W(element))</div><div class=""><br class=""></div><div class="">&nbsp; mutating func insert(element: W.Element) { self.storage.insert(W(element))</div><div class=""><br class=""></div><div class="">}</div><div class=""><br class=""></div><div class="">// step 3: per each customized equality/hash you need, write a wrapper;</div><div class="">// e.g., here is a complete “ObjectPointer” wrapper as per the original request:</div><div class="">struct ObjectPointer&lt;T:AnyObject&gt; : SetValueWrapper {</div><div class=""><br class=""></div><div class="">&nbsp; typealias Value: T</div><div class=""><br class=""></div><div class="">&nbsp; let value: T</div><div class="">&nbsp;&nbsp;</div><div class="">&nbsp; init(_ value: T) { self.value = value }</div><div class=""><br class=""></div><div class="">&nbsp; var hashValue: Int { return ObjectIdentifier(value).hashValue</div><div class=""><br class=""></div><div class="">}</div><div class=""><br class=""></div><div class="">func ==&lt;T&gt;(lhs: ObjectPointer&lt;T&gt;, rhs: ObjectPointer&lt;T&gt;) -&gt; Bool {</div><div class="">&nbsp; return lhs.value === rhs.value</div><div class="">}</div><div class=""><br class=""></div><div class="">…and you’re done; the cost is basically one tedious session of re-implementing the Set-related APIs you want on your wrapper, and then one (short!) wrapper for each custom equality/hash combo you need.</div><div class=""><br class=""></div><div class="">This isn’t *great*, but it seems perfectly-reasonable to me when weighed against the drawbacks of a `Set`-like thing that took custom logic in its `init`.</div><div class=""><br class=""></div><div class="">With conditional-conformances in place, you can also improve your quality of life a lot; it’s a bit tricky, but you could — if conditional conformances work as I expect they will — use some trickery to “punch out” `Equatable` and `Hashable`, like so:</div><div class=""><br class=""></div><div class="">/// Basic protocol for “this is a wrapper”.</div><div class="">protocol ValueWrapper {</div><div class="">&nbsp; typealias Value</div><div class="">&nbsp; var value: Value { get }</div><div class="">&nbsp; init(_ value: Value)</div><div class="">}</div><div class=""><br class=""></div><div class="">/// Specialized-wrapper-of-wrapper that is meant to source:</div><div class="">/// - Equatable, Hashable from the wrapped-value</div><div class="">/// - everything else (as needed) from the wrapped-value’s wrapped-value</div><div class="">///</div><div class="">/// …which means we can keep adding utility conformances here based on `W.Value`’s implementations,</div><div class="">/// while still having “punched-out” W’s native possible `==` and `hashValue` implementations in favor of</div><div class="">/// whatever implementations thereof are supplied by W.</div><div class="">struct WrapperWrapper&lt;W:protocol&lt;WrappedValue,Equatable,Hashable&gt; where W.Value:WrappedValue&gt; : WrappedValue {</div><div class=""><br class=""></div><div class="">&nbsp; typealias Value = W.Value // note it somewhat hides the existence of the inner wrapper&nbsp;</div><div class="">&nbsp;&nbsp;</div><div class="">&nbsp; private let storage: W</div><div class="">&nbsp; var value: Value { return self.storage.value }</div><div class=""><br class=""></div><div class="">&nbsp; init(_ value: Value) { self.storage = W(value) }</div><div class="">&nbsp;&nbsp;</div><div class="">&nbsp; // note it uses the wrapper’s (customized) hashValue implementation</div><div class="">&nbsp; var hashValue: Int {</div><div class="">&nbsp; &nbsp;return self.storage.hashValue&nbsp;</div><div class="">&nbsp; }</div><div class=""><br class=""></div><div class="">}</div><div class=""><br class=""></div><div class="">// note again this uses the *wrapper*’s implementation:</div><div class="">func ==&lt;W&gt;(lhs: WrapperWrapper&lt;W&gt;, rhs: WrapperWrapper&lt;W&gt;) -&gt; Bool {</div><div class="">&nbsp; return lhs.storage == rhs.storage&nbsp;// use wrapper’s (customized) equality</div><div class="">}</div><div class=""><br class=""></div><div class="">// but now we can start adding nice-to-have conformances based on `W.Value`</div><div class="">extension WrapperWrapper:CustomStringConvertible where W.Value:CustomStringConvertible {</div><div class="">&nbsp;&nbsp;</div><div class="">&nbsp; var description: String { return self.value.description }</div><div class=""><br class=""></div><div class="">}</div><div class=""><br class=""></div><div class="">// and so on and so forth, as-needed...</div><div class=""><br class=""></div><div class="">Although this still doesn't free you up from writing the “custom ==/hash wrappers”, if you rewrite the set-wrapper from step 2 in terms of a `WrapperWrapper` (hopefully with a better name!), then the values that are stored in the Set will pick up protocol conformances of interest from the underlying value, and thus trigger any conditional-conformances that are defined e.g. on `Set`, making it easy for you to add them to your own wrapper if you want, and so on.</div><div class=""><br class=""></div><div class="">Is this *great*? Arguably not, but I think it’s a reasonable situation, especially since the tedious parts (set-wrapper, wrapper-wrapper) are each write-once, re-use often, and the per-customization chores are really short (~5-10 lines, mostly boilerplate).</div><div class=""><br class=""></div><div class="">And again, you don’t *need* the set-wrapper or wrapper-wrapper, they just streamline the sites-of-use of such constructs.</div><div class=""><br class=""></div><div class="">Some form of actual “struct inheritance” might reduce the need to manually emulate it with protocols like the above, but protocols + conditional-conformance let you emulate enough of that feature to work out “OK” in this case, I think.&nbsp;</div><br class=""><div><blockquote type="cite" class=""><div class="">On Feb 18, 2016, at 4:58 PM, Jacob Bandes-Storch via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" class=""><div class="">Would it make sense for the standard library Set to provide variants (or parallel versions of the same data structure) that take custom hashValue/== implementations at init time (functions taking in Elements), rather than relying on Hashable/Comparable protocols?</div><div class=""><br class=""></div><div class="">Use case: I want a set of objects that are compared for equality using === rather than ==. This doesn't seem possible today, using Set, without creating some sort of wrapper object.</div><div class=""><br class=""></div><div class="">This particular case would be analogous to using NSHashTable with NSPointerFunctionsObjectPointerPersonality. (Maybe all I'm asking for is a Swiftier API for NSHashTable — including ArrayLiteralConvertible, using generics instead of UnsafePointer&lt;Void&gt;, etc.)</div><div class=""><br class=""></div><div class="">Similarly, C++'s <a href="http://en.cppreference.com/w/cpp/container/unordered_map" class="">unordered_map</a>&nbsp;and friends have template parameters specifying the hash function and equality comparator, which use std::hash and == by default.</div><div class=""><br class=""></div><div class="">(Apologies if this has been discussed already; I haven't seen it.)<br clear="all" class=""><div class=""><div class="gmail_signature"><div dir="ltr" class=""><div class="">Jacob<br class=""></div></div></div></div>
</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>