Getting generic parameter clause

Hi

I'm trying to get the generic parameter clause type, and I'm not sure if/how the best way to go about it.

struct SomeGenericModel<T: MyProtocol> {
    var a: [T]
}

I have a generic function that I pass the GenericModel to, so the type is SomeGenericModel<SomeDifferentType>. Is there a way to obtain the type of the clause is SomeDifferentType?

What does the function signature look like?

If it's a generic function, you can use the function's generic parameter:

func foo<T>(_: SomeGenericModel<T>) {
  // Use T.self
}

Apologies for the late reply

I'm converting all responses from this API that are arrays to this generic struct

struct ArrayStruct<T:MyCodableProtocol> : MyArrayProtocol {
    var data: [T]
}

so I have a generic function which sends requests and returns the decoded generic object

func send<T: MyCodableProtocol>(...) -> EventLoopFuture<T> {
    let model = try Self.decodeRelevantType(T: T.self, response: response, decoder: self.decoder)
}

decodeRelevantType signature is this

func decodeRelevantType<T: MyCodableProtocol >(T: T.Type, response: Data, decoder: JSONDecoder) throws -> T {
switch(T) {
        case is MyArrayProtocol.Type:
            let x = try decode(response: response, decoder: decoder, as: T.self)
            return ArrayStruct(data: x) as! T
}

So at the point where my case hits any array response type. The type is ArrayStruct<T>. Where I call as: T.self, is there anyway to get the type of the data inside the ArrayStruct. I'm assuming Type Erasure at this point, but I also want to make sure I'm not over complicating something.

I did have the case use the explicit ArrayStruct.Type but it wanted the generic clause, which looking at this post, it doesn't seem you can do [Pitch] Improving unspecified generic usability

I haven't worked through what you're doing here, but for a start, you've named at least four different things T, and many are shadowing each other, so I would start with naming them something more descriptive, or at least use different letters of the alphabet. That will help you figure out what you need to do.

Is there anything on MyArrayProtocol that refers to T, like associated types? If not, I don't think there's any safe way to extract T. The T doesn't even know if it is an ArrayStruct<T>.


Also, what are you doing inside decodeRelevantType? You're decoding a T, create ArrayStruct<T> from it, then force it to be T. There's something fishy going on there.

From that alone, I suspect that you don't actually need all the type gymnastics you queried in the beginning.

1 Like

Hi @Lantua No there isn't anything on the array protocol. I used that simply so I could use it in the case is MyArrayProtocol.Type because the compiler wouldn't let me use a partial generic of case is ArrayStruct.Type as it said ArrayStruct requires argument in in <...>.

Inside decodeRelevantType not much really. Just this return try decoder.decode(T.self, from: response). Which I know won't work as it will just try to decode the array of results into ArrayStruct<TypeOfArray> when really I just want to decode the response into [TypeOfArray]. So that's really my main question, is there a way to get that generic parameter clause. Almost like T.Type.Type kind of thing?

I do agree, I'm doing lots of gymnastics (my head hurts) here and have probably over complicated this and maybe gone down the rabbit hole too far. There's probably a simpler way

Hi @Lantua No there isn't anything on the array protocol. I used that simply so I could use it in the case is MyArrayProtocol.Type because the compiler wouldn't let me use a partial generic of case is ArrayStruct.Type as it said ArrayStruct requires argument in in <...>.

Inside decodeRelevantType not much really. Just this return try decoder.decode(T.self, from: response). Which I know won't work as it will just try to decode the array of results into ArrayStruct<TypeOfArray> when really I just want to decode the response into [TypeOfArray]. So that's really my main question, is there a way to get that generic parameter clause. Almost like T.Type.Type kind of thing?

I do agree, I'm doing lots of gymnastics (my head hurts) here and have probably over complicated this and maybe gone down the rabbit hole too far. There's probably a simpler way.

Edit

I think I fixed this in a simpler fashion.

// 1. Return an array of objects instead of wrapping them in `ArrayStruct`
// 2. Add conformance to Array
extension Array: MyCodableProtocol where Element: MyCodableProtocol {}

So now I can adjust the API return routes to return EventLoopFuture<[SomeArray]> and just use the standard decoder usage return try decoder.decode(TM.self, from: response). So it handles both Array and non array types with no switches etc. Seems to work ok, all my tests are working :slight_smile:

Thanks for helping

There's one more thing I totally forgot to ask. Are you trying to create a codable experience of sorts? I noticed the name MyCodableProtocol. If so, is there any reason not to use standard Codable?

If you switch to standard Codable, you'll immediately get its benefits, like encode/decode synthesis, third party library supports, etc. It is pretty versatile, only requiring the (nested) container to be unkeyed, stringly keyed, or intly keyed. I don't see any immediate obstacle in creating your own ResponseEncoder and ResponseDecoder.

1 Like

MyCodableProtocol just conforms to Codable anyway. Thank you very much for your help :slight_smile:

If it's not adding any requirement, I'd still suggest a plain Codable. You can already add default implementations right into Codable.

If there are collisions (you want to change encode behaviour), my personal suggestion is to use a wrapper type instead, and sprinkle it with property wrapper.

Using an empty protocol could only add to confusion.