How am I supposed to associate a generic type with a protocol?
Suppose I have a protocol Base which represents some type of specialized matrix or lattice, or something similar. There can be different ways to index the data, which can be implemented as different structs conforming to the protocol. There also needs to be a few associated types that can store additional data, say, List<T: Comparable>. List and other types are closely associated with how the implementation of Base is implemented and how its data is indexed, and exists 1:1 with the underlying Base (for some given Comparable type T, there is only one possible List per implementation of Base).
I need to be able to construct Lists of some type, for example in a generic function:
What about it? I'm not sure this is a higher-kinded type. It's just a normal generic type where one of the type parameters is going to be filled in from a different location than the rest of the parameters.
You sound flustered. But if you don't read at least one informative post, then it will take you infinite time, not just one month, to realize that what you want is impossible, and instead, that overloading is the closest solution available.
E.g.
protocol Base {
associatedtype List: Collection where List.Element: Comparable
}
func foo<T: Base, U: Comparable>(a: T, b: U) -> [U]
where T.List == [Never] {
typealias List = Array
var _: List<Int>
var _: List<String>
return [b]
}
struct ArrayBase: Base {
typealias List = [Never]
}
This doesn't make sense to me, a T.List<U> is a specialized data type that relates some data structure T like a lattice to a value of some arbitrary type U, it's almost never going to be an alias for an existing data type. In very simple cases you might be able to alias T.List<U> to Dictionary<T, U>.
The reason I'm skeptical this implicates higher-kinded type systems is that "List" and other "nested" types could be written as top-level types, except for the need to express that, for some given U, there is a 1:1 relation from an implementation of Base T to an implementation of List<T, U>. I'm not sure why more sophisticated type logic and reasoning would become necessary.
It's already possible to write statements like
struct Foo<T> {
struct Bar<U> {
}
}
And further, protocols can require parameterized functions, but for some reason cannot denote parameterized structs? This seems like an arbitrary constraint rather than a limitation of the type system, and the way the error is written led me to believe there's a different but "correct" syntax that does this, perhaps involving some special usage of associatedtype or primary associated types. I find it difficult to believe that people are writing complicated Swift programs without a way to parameterize structs.
You just answered your own question essentially. Today, G<U> always refers to some concrete G. If the left-hand side of the < becomes parameterized, it really does require more sophisticated type logic and reasoning.
That’s one way to think about it, but the point is that in the generics implementation, nested scopes are flattened and erased. The fact that Bar is nested within Foo does not play a role after name lookup.
Correct, the 1:1 association only exists until compile time, but in order for this to compile at all, the functions need to know that FooBar<T, U> is the list associated with Foo<T> that other functions will be using, as opposed to some other struct like FooBaz<T, U>.
This role of associating two types together is normally performed by associatedtype, but that seems to be arbritarially restricted here.
I realize that this is only a simplification of your actual use case, but if it so happens that you only need to instantiate the associated type at a fixed set of types like Int and String, a blunt way to work around the limitation could be to have multiple associated types, so you have:
You might also try seeing if U can be hoisted into a separate associated type of the conformance. Would it be possible to express this as something like func foo<T: Base>(a: T, b: T.U) -> T.ListOfU, and then have the type T which conforms to Base be generic over U?
extension MyBase<Element>: Base {
typealias U = Element
typealias ListOfU = Array<Element>
}
For now I can get away with only needing two types, but that still means maintaining two copies of the same code just because generics don't work in this situation...
I think the solution that'll end up working is a combination of your solution with AnyHashable, since these types are always Hashable, and a wrapper around it to cast the AnyHashable value to the desired type.