Generic parameter T could not be inferred. Factory methods - swift

Can someone explain to me why this wouldn't work?
I have a class with factory methods like this:
public class NetworkTask<T> {
var request: URLRequest
var completionHandler: NetworkResponse<T> -> Void
init(request: URLRequest, completionHandler: NetworkResponse<T> -> Void) {
self.request = request
self.completionHandler = completionHandler
}
static func dataResponseTaskWithRequest(request: URLRequest, completionHandler: NetworkResponse<NSData> -> Void) -> NetworkTask<NSData> {
return NetworkTask<NSData>(request: request, completionHandler: completionHandler)
}
static func mappedObjectResponseTaskWithRequest<MappedType>(request: URLRequest, completionHandler: NetworkResponse<MappedType> -> Void) -> NetworkTask<MappedType> {
return NetworkTask<MappedType>(request: request, completionHandler: completionHandler)
}
}
Then, after happily knowing that it compiles, I go to create a Task like this:
let task = NetworkTask.dataResponseTaskWithRequest(URLRequest()) { (response) in
}
Nope...
Generic parameter T could not be inferred
Wait, I can clearly infer it, the method returns NetworkTask<NSData>, so T is NSData.
Ok... then, maybe like this?
let task: NetworkTask<NSData> = NetworkTask.dataResponseTaskWithRequest(URLRequest()) { (response) in
}
Nope...
Cannot invoke 'dataResponseTaskWithRequest' with an argument list of type '(URLRequest, (_) -> _)'
Ok, so maybe the other method:
let task = NetworkTask.mappedObjectResponseTaskWithRequest(URLRequest()) { (response: NetworkResponse<String>) in
}
Nope...
Cannot convert value of type '(NetworkResponse) -> ()' to expected
argument type 'NetworkResponse<_> -> Void'
I must be clearly missing something here because the compiler can't have so many errors. Does anybody have any clue?

NetworkTask<T> is the type, not NetworkTask. That is, the parameter T is on the class and everything you do with that class, also to access its class methods, require describing that type.
Even though the T is not included in the method declaration that gives you the compiler error, there is no class NetworkTask that would contain all the class methods where the type parameter is not included – imagine instead that the method is on all classes NetworkTask<T> for any value of T. This is similar to C++ where the corresponding is even called "template", meaning that your class declaration with generic type parameters is used as a template for literally compiling different classes. This is different to for instance Java where the generics syntax is just compile time sugar with type erasure (you could indeed there call the class method – only one class really exists in that case).
Here's a minimal example to demo this some more:
class A<T> {
class func foo() {
}
class func bar(t:T) -> Void {
}
}
class B {}
A.foo() // this gives an error because the type cannot be inferred.
A.bar(1) // this works fine without compiler errors as the integer literal type can be inferred there.
In the example case above, A would be fine to called for instance with:
A<IntegerLiteralType>.foo()
You should perhaps consider whether the methods in this case belong in the class that has that type parameter T, or whether they should have something else as a receiver (or indeed whether they should be free functions?).

Related

Reference to generic function in Swift

In Swift, you can create a reference to a function in the form of a closure. For example:
func simpleFunc(param: Int) {
}
let simpleFuncReference = simpleFunc(param:) // works just fine
But in one case, I have a function with a generic parameter like this:
func hardFunc<T: StringProtocol>(param: T) {
}
let hardFuncReference = hardFunc(param:) // "Generic parameter 'T' could not be inferred"
To try to remove that error, I attempted to explicitly specify the type, but immediately another error comes up.
func hardFunc<T: StringProtocol>(param: T) {
}
let hardFuncReference = hardFunc(param:) // "Cannot explicitly specialize a generic function"
Is there a way I can get a reference to hardFunc as a closure?
As you already guessed, you have to help type inference out a little:
func hardFunc<T: StringProtocol>(param: T) {
}
let hardFuncReference:(String) -> Void = hardFunc(param:)
Note that you do have to specify the particular type that you're specializing on, but in this case you do it by specifying the type of the variable you're assigning the closure to.
You can't keep it generic unless you're in a generic context specifying that it's generic on the same type. So this will work too
struct Foo<T: StringProtocol> {
let hardFuncReference:(T) -> Void = hardFunc(param:)
}

Result type with generic Success Type

I'm switching over a project from a custom Result type to the native Swift Result type and keep running into this - or similar - error messages:
Member 'success' in 'Result<_, Error>' produces result of type 'Result<Success, Failure>', but context expects 'Result<_, Error>'
protocol DataFetcher {
func request<T>(completion: #escaping (Result<T, Error>) -> Void )
}
struct RandomFetcher: DataFetcher {
func request<String>(completion: #escaping (Result<String, Error>) -> Void) {
let str = "Hello"
completion(.success(str))
}
}
The idea is to have make a bunch of generic Fetchers for different data extraction calls and to pass these to VC's who would have a var fetcher: DataFetcher property. The VC's know which concrete types they expect from their request. I can't use an associated type as I need to save a bunch of these in an array and I thought I could get away with just the generic implementation - but it almost seems as if the Result type being declared as a generic in the protocol, means that it won't accept when I specify it in the implementation. What am I missing?
func request<String>(completion: #escaping (Result<String, Error>) -> Void) {
This is a classic generics mistake in Swift. This does not mean "request requires T==String." This means "there is an arbitrary type called String that this function accepts." There is no relationship between this String and Swift.String.
What you're trying to do here violates the protocol. The protocol says that the caller gets to pick any T they want. You can't conform to that protocol with a restricted T.
If you want the conforming type (RandomFetcher) to get to decide the type of T, then you have to use an associatedType (that's what associatedType means).
The thing you're trying to build is pretty common, but not trivial, and requires a different way of thinking about the problem. I walk through it step-by-step in this series.
In this case an associated type is preferable
protocol DataFetcher {
associatedtype FetchType
func request(completion: #escaping (Result<FetchType, Error>) -> Void )
}
struct RandomFetcher: DataFetcher {
func request(completion: #escaping (Result<String, Error>) -> Void) {
let str = "Hello"
completion(.success(str))
}
}

Cannot explicitly specialize a generic function, multiple generics

There are a lot of answers surrounding my issue, but the solutions I have tried for them have not quite solved the problem. I'm not sure if it has something to do with having multiple generics or something else (also newer to Swift so still wrapping my head around syntax).
I noticed a lot of commonality in my API request code so I decided to abstract the main work out and have it work with any codable request/response objects. Here is my main request method:
private func sendRequest<T: Encodable, U: Decodable>(url: URL, requestModel: T) -> Promise<U>
And I am trying to call it as such:
public func signIn(requestModel: SignInRequest) -> Promise<SignInResponse> {
let url = URL(string: authURL + "/signin")!
//getting compile error "Cannot explicitly specialize generic function" on this line
return sendRequest<SignInRequest, SignInResponse>(url: url, requestModel: requestModel)
}
I've tried assigning directly to the return object:
let x : Promise<SignInResponse> = sendRequest(...)
return x
to help the compiler out (as suggested in other solutions) but still same issue. Any insights?
I ended up using a solution based on this post: https://stackoverflow.com/a/36232002/1088099
I explicitly passed in the request and response object types as parameters:
private func sendRequest<T: Encodable, U: Decodable>(requestModelType: T.Type, responseModelType: U.Type, url: URL, requestModel: T) -> Promise<U>
and calling:
public func signIn(requestModel: SignInRequest) -> Promise<SignInResponse> {
let url = URL(string: authURL + "/signin")!
return sendRequest(requestModelType:SignInRequest.self,
responseModelType:SignInResponse.self,
url: url,
requestModel: requestModel)
}
Not as clean of a solution I was hoping for, but works.
Inference will succeed if you give enough context with the function's arguments to determine all generic parameters.
From you code, I suspect this is not the case as neither does the generic type U neither appear as an argument, nor there is an additional type constraint to specify it. Therefore, the compiler can't infer what is the concrete type of your function's return value from a function call.
Hence, if there's a relationship between T and U, you may use it as an additional constraint to "help" the compiler infer U once it found T. Here is a minimal example:
protocol Foo {
associatedtype Bar
var bar: Bar { get }
}
struct FooImpl: Foo {
let bar: Int = 0
}
func f<T, U>(foo: T) -> U where T: Foo, U == T.Bar {
return foo.bar
}
let a = f(foo: FooImpl())
Protocol Foo has an associated type Bar. By using this association in the function f's signature to add a constraint on its return type, Swift's compiler is now able to fully infer the specialized signature of f when I call it with an instance of FooImpl.
In your particular example, I would suggest writing a protocol to which the requestModel parameter should confirm, with an associated type to place a constraint on the return value.
For instance:
protocol RequestModel: Encodable {
associatedtype ResponseModel: Decodable
}
struct AnAwesomeModel: RequestModel {
typealias ResponseModel = String
}
private func sendRequest<T: RequestModel, U>(url: URL, requestModel: T) -> Promise<U>
where U == T.ResponseModel
{
// ...
}
// x's type will be inferred as String
let x = sendRequest(url: URL("http://...")!, requestModel: AnAwesomeModel())

Using Swift function that a function that takes a generic sequence

I'm picking swift up now and the generics are pretty different than what I'm used to. What is the right way to do something like this?
func createThing<T, Seq: Sequence>(_ type: T.Type, _ block : #escaping (_ sequence: Seq) -> Void) where Seq.Element == T {
// ...
}
enum MyEnum {
case A
case B
}
// error: generic parameter 'Seq' could not be inferred
createThing(MyEnum.Type, { sequence in
for i in sequence{
//...
}
})
I would love to just supply a generic type parameter directly with createThing<MyEnum>(...) but that apparently isn't something Swift can do and generics seem to work pretty different for protocols than they do everything else.
Seq is part of the generic signature of the createThing function, which means that the compiler either needs to be able to infer this from the calling context, or be explicitly be told what concrete implementation of Sequence should expect. Also placing the Sequence generic at the function level really limits what you can do within that function, since it cannot instantiate a protocol.
You can convert the (Seq) -> Void block to a (T) -> Void one, and move the sequence iteration in doSomething, this will remove the compile error. And while you're at it, you can add a default value for the type parameter, this will enable type inferring and automatic filling of that parameter
func createThing<T>(_ type: T.Type = T.self, _ block: (T) -> Void) {
// ...
// assuming sequence is create above
sequence.forEach(block)
}
enum MyEnum {
case a
case b
}
// a dedicated function for processing items also means better structured code :)
func processEnum(_ value: MyEnum) {
// do your stuff
}
// you can now pass only the second argument
createThing(processEnum)

Swift 3: Nested Generic Type

I've got some weird behavior from the compiler I can't figure out (Xcode 8, Swift 3) as I'm trying to migrate my code from Swift 2. I think it has to do with tuples, but I'm not entirely certain.
I've got a generic class that defines a couple generics. In those generics, I've also got a couple type alias's setup and a function that uses them:
open class GuardPool <Key: Hashable, Resource> {
public typealias ResourceCallback = ([Resource]) -> Void
public typealias Request = (keys: Set<Key>, cb: ResourceCallback)
fileprivate var pendingRequests: [Request] = []
open func request(_ keys: Set<Key>, cb: ([Resource]) -> Void) {
let pendingRequest: Request = (keys: keys, cb: cb)
pendingRequests.append(pendingRequest)
}
}
On the assignment (let pendingRequest: Request = ...), I get this error:
Cannot convert value of type 'Set<Key>' to specified type 'Set<_>'
I cannot figure out how to fix this. It seems like the compiler can't recognize the type information for Set.
Note: Obviously, the class is much larger that this. I copied and pasted out the relevant code instead of inserting 300 lines.
The error message is misleading. If you remove
the (unnecessary) explicit type annotation in the second line
let pendingRequest = (keys: keys, cb: cb)
then the compiler states that
error: non-escaping parameter 'cb' may only be called
note: parameter 'cb' is implicitly non-escaping
which reveals the actual problem: The cb parameter must be marked as #escaping because
it is stored in a property and might be called at a later time,
after returning from the request method:
open func request(_ keys: Set<Key>, cb: #escaping ([Resource]) -> Void) {
let pendingRequest = (keys: keys, cb: cb)
pendingRequests.append(pendingRequest)
}
In Swift 2, closures were escaping by default and could be marked
with #noescape. In Swift 3, closures are non-escaping by default,
compare SE-0103: Make non-escaping closures the default.