> 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 _______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
