Generics + Protocols: I'm having trouble with a design I've been toying around with

I’m working with a remote key/value store and use the following type to request data from the store:

struct Request<K: Codable & Hashable, V: Codable> { ... }

I’ve then defined a protocol that will dispatch the request and return the result:

protocol Service {
    var session: Session { get }
    func dispatch<K, V, R>(request: CloudMapRequest<K, V>, returnType: R.Type)
        throws -> Promise<R?> where K: Codable & Hashable, V: Codable, R: Decodable
}

This part compiles fine. However, when I try to use the API:

// K, V is defined on the class that contains the above invocation.
let req = Request<K, V>(name: name, operation: Operation.clear)
service.dispatch(request: req, returnType: nil as String.Type)

results in the following compilation failure due to the new call:

Cannot convert value of type 'Request<K, V>' to expected argument type 'Request<_, _>'

So it seems I’m not providing enough type info to the dispatch function that results in the above?
Any suggestions on how to resolve this?

What does that mean? Would the code compile if you replaced the value with String.self?

That was some cruft from some earlier work. I’ve cleaned that up. And it turns out the protocols aren’t relevant to my issue. I’m missing something in my understanding of generics with Swift.

I’ve removed the protocol and am calling the method directly on the Service instance.

So here’s the simplified version:

class Service<K, V> where K: Codable & Hashable, V: Codable {
    func dispatch<R>(request: Request<K, V>, returnType: R.Type? = nil)
    throws -> Promise<R?> where R: Decodable { ... }
}

Service.dispatch() is invoked from within another generic class (using the same K, V and constraints).

public class CloudMap<K, V> where K: Codable & Hashable, V: Codable {
    ...
    public func clear() throws {
        let req = Request<K, V>(name: name, operation: Operation.clear)
        let _ = try service.dispatch(request: req)
    }
    ...
}

The resulting error message is the same as that in the original post:

Cannot convert value of type 'Request<K, V>' to expected argument type 'Request<_, _>'

Indeed, the error matches the sample code. Service dispatch expects a “CloudMapRequest” where you are passing in a “Request”. 2 different types.

Typos. Trying to shoring the text - unsuccessfully.

Should be corrected now.

Can you post a single block of text (or link to a gist, or similar) that can be used to reproduce the issue? For example, something that can be pasted into a playground or pasted into a file and compiled on the command line.

I don’t see definitions of “name” or “Operation” here, and those types are certainly important to understand what’s going on here.

Putting your sample code together, I haved arrived at this:

struct Request<K: Codable & Hashable, V: Codable> {}

class Service<K, V> where K: Codable & Hashable, V: Codable {
    func dispatch<R>(request: Request<K, V>, returnType: R.Type? = nil)
    throws -> R? where R: Decodable {
        return nil
    }
}

public class CloudMap<K, V> where K: Codable & Hashable, V: Codable {

    public func clear() throws {
        let req = Request<K, V>()
        let service = Service<K, V>()
        let _ = try service.dispatch(request: req)
    }
}

Here, R is clearly underspecified, since we only know it’s Decodable, and that’s not enough. This makes the code compile:

let _ = try service.dispatch(request: req, returnType: String.self)

As @zoul highlights, R cannot be inferred when the default value is issued. I suggest implementing the default the “old way”, as in

func dispatch<R>(request: Request<K, V>, returnType: R.Type)
    throws -> R where R: Decodable { ... }
func dispatch(request: Request<K, V>)
    throws { ... }