> On Mar 16, 2017, at 10:23 AM, Joe Groff via swift-evolution > <[email protected]> wrote: > > >> On Mar 16, 2017, at 10:21 AM, Itai Ferber <[email protected]> wrote: >> >> On 15 Mar 2017, at 19:12, Joe Groff wrote: >> >> >> On Mar 15, 2017, at 6:46 PM, Itai Ferber <[email protected]> wrote: >> >> Thanks Joe, and thanks for passing this along! >> >> To those who are curious, we use abstract base classes for a cascading list >> of reasons: >> >> • We need to be able to represent keyed encoding and decoding containers as >> abstract types which are generic on a key type >> • There are two ways to support abstraction in this way: protocol & type >> constraints, and generic types >> • Since Swift protocols are not generic, we unfortunately cannot write >> protocol KeyedEncodingContainer<Key : CodingKey> { ... }, which is the >> "ideal" version of what we're trying to represent >> • Let's try this with a protocol first (simplified here): >> >> protocol Container { >> associatedtype Key : CodingKey >> } >> >> func container<Key : CodingKey, Cont : Container>(_ type: Key.Type) -> Cont >> where Cont.Key == Key { >> // return something >> } >> >> This looks promising so far — let's try to make it concrete: >> >> struct ConcreteContainer<K : CodingKey> : Container { >> typealias Key = K >> } >> >> func container<Key : CodingKey, Cont : Container>(_ type: Key.Type) -> Cont >> where Cont.Key == Key { >> return ConcreteContainer<Key>() // error: Cannot convert return expression >> of type 'ConcreteContainer<Key>' to return type 'Cont' >> } >> >> Joe or anyone from the Swift team can describe this better, but this is my >> poor-man's explanation of why this happens. Swift's type constraints are >> "directional" in a sense. You can constrain a type going into a function, >> but not out of a function. There is no type I could return from inside of >> container() which would satisfy this constraint, because the constraint can >> only be satisfied by turning Cont into a concrete type from the outside. >> >> Okay, well let's try this: >> >> func container... { >> return ConcreteContainer<Key>() as! Cont >> } >> >> This compiles fine! Hmm, let's try to use it: >> >> container(Int.self) // error: Generic parameter 'Cont' could not be inferred >> >> The type constraint can only be fulfilled from the outside, not the inside. >> The function call itself has no context for the concrete type that this >> would return, so this is a no-go. >> >> • If we can't do it with type constraints in this way, is it possible with >> generic types? Yep! Generic types satisfy this without a problem. However, >> since we don't have generic protocols, we have to use a generic abstract >> base class to represent the same concept — an abstract container generic on >> the type of key which dynamically dispatches to the "real" subclassed type >> >> Hopes that gives some simplified insight into the nature of this decision. >> >> I see. Protocols with associated types serve the same purpose as generic >> interfaces in other languages, but we don't have the first-class support for >> protocol types with associated type constraints (a value of type `Container >> where Key == K`). That's something we'd like to eventually support. In other >> places in the standard library, we wrtie the type-erased container by hand, >> which is why we have `AnySequence`, `AnyCollection`, and `AnyHashable`. You >> could probably do something similar here; that would be a bit awkward for >> implementers, but might be easier to migrate forward to where we eventually >> want to be with the language. >> >> -Joe >> >> Yep, that’s a good way to describe it. >> We could potentially do that as well, but adding another type like >> AnyHashable or AnyCollection felt like a much more sweeping change, >> considering that those require some special compiler magic themselves (and >> we’d like to do as little of that as we can). > > AnyCollection doesn't have any special compiler magic. AnyHashable's only > magic is that it has implicit conversions, but that would become normal > behavior once it can be replaced by a plain Hashable existential type.
Hey Itai. I am not sure if I missed this. But did you follow up with why you didn't want to use AnyCollection/AnyHashable? The thread got really long pretty fast. Michael > > -Joe > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution _______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
