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