On 17 Mar 2017, at 12:18, Michael Gottesman wrote:
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.
I responded to this in a different part of the thread very recently. Can
you elaborate on how a type like `AnyCollection`/`AnyHashable` would
help here? More important than the type erasure is the type being
generic on the key type, and this must be specified. How would this be
possible?
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