> On Jun 7, 2016, at 7:51 PM, Dave Abrahams via swift-evolution > <swift-evolution@swift.org> wrote: > > > on Tue Jun 07 2016, Matthew Johnson <matthew-AT-anandabits.com> wrote: > >>> On Jun 6, 2016, at 12:22 AM, Dave Abrahams <dabrah...@apple.com> >> wrote: >>> >>> >>> on Sun Jun 05 2016, Matthew Johnson <matthew-AT-anandabits.com >> <http://matthew-at-anandabits.com/>> wrote: >>> >> >>>> Sent from my iPhone >>>> >>>>> On Jun 5, 2016, at 3:51 PM, Dave Abrahams via swift-evolution >>>>> <swift-evolution@swift.org> wrote: >>>>> >>>>> >>>>>> on Wed May 25 2016, Matthew Johnson <swift-evolution@swift.org> wrote: >>>>>> >>>>>> Sent from my iPad >>>>>> >>>>>>> On May 25, 2016, at 12:10 PM, Jordan Rose via swift-evolution >>>>>>> <swift-evolution@swift.org> wrote: >>>>>>> >>>>>>> >>>>>>>>> On May 25, 2016, at 05:27, Brent Royal-Gordon via swift-evolution >>>>>>>>> <swift-evolution@swift.org> wrote: >>>>>>>>> >>>>>>>>> AFAIK an existential type is a type T with type parameters that >>>>>>>>> are still abstract (see for example >>>>>>>>> https://en.wikipedia.org/wiki/Type_system#Existential_types), >>>>>>>>> i.e. have not been assigned concrete values. >>>>>>>> >>>>>>>> My understanding is that, in Swift, the instance used to store >>>>>>>> something whose concrete type is unknown (i.e. is still abstract), >>>>>>>> but which is known to conform to some protocol, is called an >>>>>>>> "existential". Protocols with associated values cannot be packed >>>>>>>> into normal existentials because, even though we know that the >>>>>>>> concrete type conforms to some protocol, the associated types >>>>>>>> represent additional unknowns, and Swift cannot be sure how to >>>>>>>> translate uses of those unknown types into callable members. Hence, >>>>>>>> protocols with associated types are sometimes called >>>>>>>> "non-existential". >>>>>>>> >>>>>>>> If I am misusing the terminology in this area, please understand >>>>>>>> that that's what I mean when I use that word. >>>>>>> >>>>>>> We’re not consistent about it, but an “existential value” is a value >>>>>>> with protocol or protocol composition type. My mnemonic for this is >>>>>>> that all we know is that certain operations exist (unlike a generic >>>>>>> value, where we also have access to the type). John could explain it >>>>>>> more formally. We sometimes use “existentials” as a (noun) shorthand >>>>>>> for “existential value”. >>>>>>> >>>>>>> In the compiler source, all protocols and protocol compositions are >>>>>>> referred to as “existential types”, whether they have associated >>>>>>> types or not. Again, a protocol asserts the existence (and >>>>>>> semantics) of various operations, but nothing else about the >>>>>>> conforming type. (Except perhaps that it’s a class.) All protocols >>>>>>> are thus “existential types” whether or not the language supports >>>>>>> values having that type. >>>>>>> >>>>>>> It is incorrect to say that protocols with associated types (or >>>>>>> requirements involving Self) are “non-existential”. >>>>>> >>>>>> I haven't heard people using this term myself, but I imagine they >>>>>> probably mean "can't form an existential value with the protocol". >>>>>> There certainly appears to be a lot of confusion in the community with >>>>>> many not realizing that this is a temporary limitation of the >>>>>> implementation, not a necessary fact. >>>>> >>>>> As far as I know there is no known way to make protocols with Self >>>>> requirements usefully “existentiable,” or whatever you want to >> call it. >>>>> So unless I'm missing something, in this respect, the limitation >> is not >>>>> temporary at all. >>>> >>>> Take a look at the Equatable example in the opening existentials >>>> section of Doug's manifesto: >>>> >> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160229/011666.html >>> >>> Yes, note that I said *usefully* “existential.” >> >> Fair enough. But note that I was only talking about the inability to >> form and open such an existential which appears likely to be a >> temporary limitation given the generics manifesto (of course things >> could change). >> >>> While we can of course downcast in this way, you have to handle the >>> failure case and it's not like you can use this to make a >>> heterogeneous Set<Hashable>. AFAICT, this is not at all like what >>> happens with associated types, where Collection<Element: Int> has >>> obvious uses. >> >> We can’t use this to form Set<Hashable> because existentials don’t >> conform to the protocol. I know there is complexity in implementing >> this and it is not possible to synthesize conformance of the >> existential for all protocols, but AFAIK it isn’t a settled point that >> we won’t try to improve the situation in some way. > > Of course. I'm just trying to point out that such existentials are > likely to be a whole lot less useful than many people might think. > >> Maybe we can make progress here somehow. > > Maybe. It's a research project. > >> In the meantime, we can make a simple wrapper type to provide the >> required conformance and make a Set<HashableWrapper> that allows us to >> put Hashable into a Set. This isn’t ideal but it is a lot less >> boilerplate than is involved in manual type erasure today where you >> need to define a struct, base class, and wrapper class. I’d say >> that’s a win even if we’d like to do better in the future. >> >> struct HashableWrapper: Hashable { >> var value: Hashable >> public var hashValue: Int { return base.hashValue } >> } >> >> public func ==(lhs: HashableWrapper, res: HashableWrapper) -> Bool { >> if let lhsValue = lhs.value openas T { // T is a the type of >> lhsValue, a copy of the value stored in lhs >> if let rhsValue = rhs.value as? T { // is res also a T? >> // okay: lhsValue and rhsValue are both of type T, which >> we know is Equatable >> if lhsValue == rhsValue { >> return true >> } >> } >> } >> return false >> } >> >> (We could also do this with a generic Wrapper<T> and conditional >> conformance in an `extension Wrapper: Hashable where T == Hashable` if >> we don’t want a bunch of wrapper types laying around) > > I don't think I"ve made my point very well. Equatable (the > Self-requirement part of Hashable) has a simple answer when the types > don't match, as I noted in the POP talk. Let me put it differently: > existentializing a protocol with Self requirements comes with > limitations that I'm betting most people haven't recognized. For > example, you won't be able to add two arbitrary FloatingPoint > existentials. I think many people view working with existentials as > “easier” or “cleaner” than working with generics, but haven't realized > that if you step around the type relationships encoded in Self > requirements and associated types you end up with types that appear to > interoperate but in fact trap at runtime unless used in exactly the > right way.
I was thinking more about that, and it seems that of all the possible generics extensions, the least ‘generic’ might be the most interesting in the short term: a type safe way to open existentials But rather than Doug, strawman suggestion: if let storedInE1 = e1 openas T { // T is a the type of storedInE1, a copy of the value stored in e1 if let storedInE2 = e2 as? T { // is e2 also a T? if storedInE1 == storedInE2 { … } // okay: storedInT1 and storedInE2 are both of type T, which we know is Equatable } } I’d rather have something like this (using the new extended ‘if/where’) if let storedInE1 = e1 as? T; let storedInE2 = e2 as? T; storedInE1 == storedInE2 { … } // okay: storedInT1 and storedInE2 are both of type T, which we know is Equatable } } which with my ongoing little pet-project might go down to if let! e1 as? T; let! e2 as? T; e1 == e2 { … } // okay: e1 and e2 are both of type T, which we know is Equatable } } > > -- > Dave > _______________________________________________ > swift-evolution mailing list > swift-evolution@swift.org > https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution