Erroneous "Generic parameter not used in function signature" error?

I'm attempt to create an extension to a PromiseKit Promise to transform a Promise<DataResponse<Request.Response> to a Promise<Request.Response>:

extension Promise {
    func responseValue<Request: RawRequestEncodable & Requestable>() -> Promise<Request.Response>
        where T == DataResponse<Request.Response> {
        return then { (response) in
            Promise<Request.Response> { (seal) in
                response.result.withValue { seal.fulfill($0) }
                               .withError { seal.reject($0) }
            }
        }
    }
}

It compiles when I incorrectly use Promise<Request> as the return type, but fails with a "Generic parameter Request is not used in function signature" error when I try to return Promise<Request.Response>. This seems incorrect. Is this a compiler bug or are my types incorrect?

You’re not using Request, you’re using Request.Response.
There’s a lot less information compiler can infer, take this example.

func aa<S: Sequence>(_: S.Element) {
    // Error: Generic parameter `S` is not used in function signature
}

aa(3) // What should `S` be?

Compiler can’t infer what S should be, there’re simply too many possibilities.

What you can do is that you refactor the function to use only Request.Response

extension Promise {
    func responseValue<Response>() -> Promise<Response>
        where T == DataResponse<Response>, <#whatever-response-should-be-like#> {
        return then { (response) in
            Promise<Response> { (seal) in
                response.result.withValue { seal.fulfill($0) }
                               .withError { seal.reject($0) }
            }
        }
    }
}

You’d even notice that the function body doesn’t even use Request, only Request.Response.

3 Likes

I think there's not enough type information, since we can't manually specify generic parameters when calling a function. If this function were to compile, how would you provide enough information for it to know what Request is? The calling context would only be enough to infer Request.Response, and the compiler can't work backward from there.

I suppose I'm a bit confused then. Response is an associatedtype of one of the protocols used to define Request. Doesn't that mean that the type of Response is dependent on Request?

@Lantua Your suggestion works, thanks! I suppose I was trying to be too particular on the generic requirements. It doesn't really matter where the Promise<DataResponse<Response>> comes from, it can always be turned into a Promise<Response>.

My final version is just this:

extension Promise {
    func responseValue<Response>() -> Promise<Response>
        where T == DataResponse<Response> {
        return then { (response) in
            Promise<Response> { (seal) in
                response.result.withValue { seal.fulfill($0) }
                               .withError { seal.reject($0) }
            }
        }
    }
}

While I'm here, @Max_Howell1, is this a good way to accomplish this?

While I don’t think of generics in term of dependencies, I’d say it’s the other way around. Int works fine on its own but you can’t work with Array unless you supply it’s associatedType (e.g. Array<Int>).

When specifying generics I tend to ask what other types would benefit from the function/instance I just wrote, then try to fine a placeholder type that encompasses all those types.

Also, whenever I see == in generic’s where clause, I have a sneaky suspicion that one can remove the placeholder type, since only one type will satisfy that requirement.

While I don’t know what name is used for DataResponse‘s placeholder type, it’d go like this:

struct DataResponse<PlaceholderName> {
    ...
}

extension Promise {
    func responseValue() -> Promise<T.PlaceholderName> {
        return then { (response) in
            Promise { (seal) in
                response.result.withValue { seal.fulfill($0) }
                               .withError { seal.reject($0) }
            }
        }
    }
}

This would also ease the compiler’s work as it wouldn’t need to infer what the placeholder type would be.

Seems like a code smell to me that you can then off something that has intrinsic “Resultiness”.

Hard to know what to suggest without seeing the code that might produce the DataResponse<Response> object. But usually in PromiseKit itself we would create an extension on the thing that produces that type and have it return a Promise, which here would be Promise<Response>.

Hope I helped!