Generic associated type

Hi,

Currently Swift allows to use a simple associated type:

protocol ExampleProtocol {

    associatedtype Element

    func exampleMethod(with element: Element)
}

But what if we want to use exampleMethod(with:) with generic Element?
To make protocols even more flexible I propose:

protocol ExampleProtocol {

    associatedtype Element<T>

    func exampleMethod<T>(with element: Element<T>)
}

Which allows to make default protocol implementation with generic associated type:

extension ExampleProtocol {

    func exampleMethod<T>(with element: Element<T>) {
        // (...)
    }
}

and allows to fullfill protocol requirements for generic nested type:

class ExampleClass: ExampleProtocol {

    enum Element<K> {
        case new(K)
        case old(K)
    }
}

Simple example:

protocol Servicing {

    associatedtype Response<K>

    func request<T>(expecting result: (_ response: Response<T>) -> Void)
}

extension Servicing {

    func request<T>(expecting result: (_ response: Response<T>) -> Void) {
        // (...)
    }
}

class StreamService: Servicing {

    enum Response<K> {
        case stream(K)
        case failure(error: Error)
    }
}

class DataService: Servicing {

    enum Response<K> {
        case data(K)
        case failure(error: Error)
    }
}

class Client {

    func streamVideo() {
        let service = StreamService()
        service.request { (response: Response<Video>) in
            switch response {
            case .failure(let error):
                print(error)
            case .stream(let data):
                break
            }
        }
    }

    func getUsername() {
        let service = DataService()
        service.request { (response: Response<String>) in
            switch response {
            case .failure(let error):
                print(error)
            case .data(let data):
                break
            }
        }
    }
}
2 Likes

It's come up before:

There are certainly use cases for it, but it's not easy to implement. I I think it counts as a limited form of "higher-kinded types", which have been discussed a few times as well (though in a slightly different form).

+1. Just encountered a case where I need this. Essentially, I need a protocol describing a Functor.

I have a code that takes collection of X, and reports the same collection, but now of Y and Z. If collection is an array of X, I should be getting arrays of Y and Z. If collection is a dictionary, I should be getting dictionaries with the same keys, but with values of Y and Z. If collection is generic struct S<T> with several named fields of type T, then sending S<X>, I should be getting S<Y> and S<Z>, etc.

Since in my case I have a limited set of target types, I was able to workaround this by making a protocol with two associated types:

class XMappable {
    associatedtype YResult
    associatedtype ZResult
    func mapX(_ f: (X) -> Y) -> YResult
    func mapX(_ f: (X) -> Z) -> ZResult
}

Sounds like Rust has now implemented exactly this:

3 Likes

Still experimental because of some scenarios to fix.
See Tracking Issue