> 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

Reply via email to