Refining generics in classes

Swift Users:
  I have a generics use case which has somewhat stumped me. I have two related protocols, JSONDecodable and CompressedDecodable, and CompressedDecodable inherits from JSONDecodable (though that relationship isn’t strictly necessary). I also have a generic function that’s overloaded for each of those protocols. I’m trying to write a class to make a network request expecting a generic response type of either JSONDecodable or CompressedDecodable. However, it doesn’t seem possible to write it in such a way that the overload I need is called. Instead, it’s always the superclass’ type’s overload that is called. For example:

protocol JSONDecodable { init() }
protocol CompressedDecodable: JSONDecodable { }

class NetworkRequest<T: JSONDecodable> {
    
    var response: T?
    
    func doAThing() {
        response = doSomething()
    }
}

class CompressedNetworkRequest<U: CompressedDecodable>: NetworkRequest<U> {
    
}

func doSomething<T: JSONDecodable>() -> T {
    print("One: \(T.self)")
    return T()
}

func doSomething<T: CompressedDecodable>() -> T {
    print("Two: \(T.self)")
    return T()
}

struct Uno: JSONDecodable { }
struct Dos: CompressedDecodable { }

NetworkRequest<Uno>().doAThing()
CompressedNetworkRequest<Dos>().doAThing()

In a playground this prints:

One: Uno
One: Dos

Ultimately, I understand why this happens (NetworkRequest’s generic type is always going to be JSONDecodable, no matter if it’s actually a subtype). Is there any way, aside from completely duplicating the class, to call the overload appropriate for the type passed in a class like this?

Jon Shier

If I change it slightly

protocol JSONDecodable { init() }
protocol CompressedDecodable: JSONDecodable { }

class NetworkRequest<T: JSONDecodable> {

    var response: T?

    func doAThing() {
      response = doSomething()
    }
    func doSomething<T: JSONDecodable>() -> T {
      print("One: \(T.self)")
      return T()
    }

}

class CompressedNetworkRequest<U: CompressedDecodable>: NetworkRequest<U> {

  override func doSomething<U: CompressedDecodable>() -> U {
    print("Two: \(U.self)")
    return U()
  }

}

struct Uno: JSONDecodable { }
struct Dos: CompressedDecodable { }

NetworkRequest<Uno>().doAThing()
CompressedNetworkRequest<Dos>().doAThing()
I do get the output:

One: Uno
Two: Dos
But I also get a crash error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=EXC_I386_GPFLT).

···


Fred

From: Jon Shier via swift-users <swift-users@swift.org>
Reply: Jon Shier <jon@jonshier.com>
Date: 16 November 2017 at 05:43:06
To: Седых Александр via swift-users <swift-users@swift.org>
Subject: [swift-users] Refining generics in classes

Swift Users:
I have a generics use case which has somewhat stumped me. I have two related protocols, JSONDecodable and CompressedDecodable, and CompressedDecodable inherits from JSONDecodable (though that relationship isn’t strictly necessary). I also have a generic function that’s overloaded for each of those protocols. I’m trying to write a class to make a network request expecting a generic response type of either JSONDecodable or CompressedDecodable. However, it doesn’t seem possible to write it in such a way that the overload I need is called. Instead, it’s always the superclass’ type’s overload that is called. For example:

protocol JSONDecodable { init() }
protocol CompressedDecodable: JSONDecodable { }

class NetworkRequest<T: JSONDecodable> {

var response: T?

func doAThing() {
response = doSomething()
}
}

class CompressedNetworkRequest<U: CompressedDecodable>: NetworkRequest<U> {

}

func doSomething<T: JSONDecodable>() -> T {
print("One: \(T.self)")
return T()
}

func doSomething<T: CompressedDecodable>() -> T {
print("Two: \(T.self)")
return T()
}

struct Uno: JSONDecodable { }
struct Dos: CompressedDecodable { }

NetworkRequest<Uno>().doAThing()
CompressedNetworkRequest<Dos>().doAThing()

In a playground this prints:

One: Uno
One: Dos

Ultimately, I understand why this happens (NetworkRequest’s generic type is always going to be JSONDecodable, no matter if it’s actually a subtype). Is there any way, aside from completely duplicating the class, to call the overload appropriate for the type passed in a class like this?

Jon Shier
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Hi Jon,

To clarify for those wondering, this is happening because the `doAThing()` method dispatches statically to `doSomething()` based on `NetworkRequests`’s `T`; since `doAThing()` isn’t overridden in `CompressedNetworkRequest`, the method is inherited directly, including that static dispatch.
To pull this example out a bit:

protocol X {}
protocol Y : X {}

class Foo<T : X> {
     var value: T

     init(value: T) {
         self.value = value
     }

     func doSomething() {
         doThing(value)
     }
}

class Bar<T : Y> : Foo<T> {}

func doThing<T : X>(_ v: T) { print("doThing<T : X>()") }
func doThing<T : Y>(_ v: T) { print("doThing<T : Y>()") }

extension Int : Y {}
Foo(value: 42).doSomething() // doThing<T : X>
Bar(value: 42).doSomething() // doThing<T : X>

In order to not inherit that static dispatch, `doSomething` needs to be overridden in `Bar`, even if to call the same function:

// …

class Bar<T : Y> : Foo<T> {
     override func doSomething() {
         doThing(value) // statically dispatches based on T : Y
     }
}

// …

Foo(value: 42).doSomething() // doThing<T : X>()
Bar(value: 42).doSomething() // doThing<T : Y>()

Unfortunately, I don’t think there’s a great way around this, short of duplicating the calls which do that sort of dispatch.
If you can somehow funnel some of these calls through one call site, it might make your life a bit easier.

— Itai

···

On 15 Nov 2017, at 20:42, Jon Shier via swift-users wrote:

  Swift Users:
  I have a generics use case which has somewhat stumped me. I have two related protocols, JSONDecodable and CompressedDecodable, and CompressedDecodable inherits from JSONDecodable (though that relationship isn’t strictly necessary). I also have a generic function that’s overloaded for each of those protocols. I’m trying to write a class to make a network request expecting a generic response type of either JSONDecodable or CompressedDecodable. However, it doesn’t seem possible to write it in such a way that the overload I need is called. Instead, it’s always the superclass’ type’s overload that is called. For example:

protocol JSONDecodable { init() }
protocol CompressedDecodable: JSONDecodable { }

class NetworkRequest<T: JSONDecodable> {

    var response: T?

    func doAThing() {
        response = doSomething()
    }
}

class CompressedNetworkRequest<U: CompressedDecodable>: NetworkRequest<U> {

}

func doSomething<T: JSONDecodable>() -> T {
    print("One: \(T.self)")
    return T()
}

func doSomething<T: CompressedDecodable>() -> T {
    print("Two: \(T.self)")
    return T()
}

struct Uno: JSONDecodable { }
struct Dos: CompressedDecodable { }

NetworkRequest<Uno>().doAThing()
CompressedNetworkRequest<Dos>().doAThing()

In a playground this prints:

One: Uno
One: Dos

Ultimately, I understand why this happens (NetworkRequest’s generic type is always going to be JSONDecodable, no matter if it’s actually a subtype). Is there any way, aside from completely duplicating the class, to call the overload appropriate for the type passed in a class like this?

Jon Shier
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users