Infer Closure Return Type from Closure Body When Working with Generics

Infer closure return type from closure body when working with generics

Actually, it seems that I was pushing the compiler limits too hard (as @Martin R pointed in the comments). The { a in } closure was kinda incomplete, since the compiler had no statements to infer the closure return type from.

This works:

Test<Int>().test { (a: Int) -> Void in () }

Same as the following:

func doNothing() { }

Test<Int>().test { (a: Int) -> Void in doNothing() }

In the above examples the compiler is provided with the minimum amount of information to determine which overload to pick.

How can I solve Unable to infer type of a closure parameter issue?

Generics parameter (T in your case) is input, so you should give on input of function all arguments typed (compiler does not jump inside to cycle through implementation to find where the type could be)

So the solution is to provide explicit type in closure, like

testValue(value: { (newValue: Bool) in      // << here !!

bool = newValue // <<: This or That!

print(newValue) // <<: This or That!

})

Tested with Xcode 13.1 / iOS 15

Update: You can keep T in function and use it inside as input argument for processing, like

func testValue<T>(value: @escaping (T) -> Void) {
if T.self == Bool.self {
let randomValue: Bool = true
value(randomValue as! T)
} else if T.self == String.self {
let randomValue: String = "Test"
value(randomValue as! T)
}
}

struct ContentView: View {
@State private var bool: Bool = Bool()

var body: some View {

Text("Hello, World!")
.onAppear() {
testValue(value: { (newValue: Bool) in
bool = newValue
print(newValue)
})
testValue(value: { (newValue: String) in
print("Got: \(newValue)")
})
}
}
}

Sample Image

Unable to infer complex closure return type with SwiftUI

The issue is not with the closure, but with the isFavorite property on landmark.

It is not declared on the Landmark type, and the compiler instead of showing the undeclared property error, unable to resolve the stacks build closure return type, so it shows and error there.

Great job Apple tutorial writers and even better one Xcode11 compiler.

To fix:

  1. Declare isFavorite variable on the Landmark type.
  2. Make sure you update the landmarkData.json for every landmark record with the isFavorite = false entry, otherwise the app will crash at runtime.

Alamofire Unable to infer closure type in the current context

func log() -> Self {
let responseSerializer = DataRequest.jsonResponseSerializer()
return response(responseSerializer: responseSerializer) { [weak self] response in
guard let _self = self else { return }
_self.printRequestString(response)
}
}

Works well.

Difference in type inference between forEach closure and for loop in Typescript

The general problem is that, given a callback passed to a function, TypeScript can't infer when the callback will be called, if ever. With

array.forEach(item => {
result.push(f(item));
});

The code that pushes to result is not in the map function proper; it's inside a callback inside the map function.

In contrast, with for loops (and with pretty much any code that doesn't involve a callback), when the logic involves just a plain function body, without callback nesting, TS can automatically understand and propagate the types as you're expecting.

I think the best approach here (not only to make TS happy, but to make your code easier to maintain in general) is to avoid callbacks with side-effects. In this case, instead of pushing to an outside array as a side-effect, use Array.prototype.map to create the new array all at once.

let map: Mapper = function (array, f) {
return array.map(f);
}

TypeScript generally works best when data is transformed in a more functional/pure manner, instead of as a result of side-effects. It's not necessary, but it makes automatic type inference easier and can help you avoid from having to explicitly note types.

Generic parameter 'T' could not be inferred with closures

You need to explicitly tell the type when calling the function. If you are expecting String then call it with String type.

MyClass.myFunction { (response: String) in

}

Cannot infer a lifetime for a closure returning a boxed trait that contains a reference

It appears that you've missed a key takeaway of your previous questions and their duplicates:

  • Lifetime annotation for closure argument
  • Cannot infer a lifetime for a struct containing a reference to a closure
  • How to declare a lifetime for a closure argument

By declaring a type on the closure argument, you stop performing type inference for the arguments. This causes a new implicit lifetime to be generated by the closure, one which does not match your requirements. Just don't declare the type at all.

Next, you need to state that your closure is going to take a reference to some bytes and return a boxed trait object that will return some bytes of the same lifetime and contains a reference of that same lifetime:

struct FragMsgReceiver<'a> {
recv_dgram: &'a for<'b> FnMut(&'b mut [u8])
-> Box<MockFutureTrait<Item = &'b mut [u8]> + 'b>,
}

See Why is Box<Iterator<Item = &Foo> + 'a> needed? for more details about the + 'a syntax.

Then update constrain_handler to match:

struct FragMsgReceiver<'a> {
recv_dgram: &'a for<'b> FnMut(&'b mut [u8])
-> Box<MockFutureTrait<Item = &'b mut [u8]> + 'b>,
}

fn constrain_handler<F>(f: F) -> F
where
F: for<'b> FnMut(&'b mut [u8])
-> Box<MockFutureTrait<Item = &'b mut [u8]> + 'b>,
{
f
}

fn main() {
let mut recv_dgram = constrain_handler(|buf| Box::new(MockFuture { item: buf }));

let fmr = FragMsgReceiver {
recv_dgram: &mut recv_dgram,
};
}

The whole thing can be made simpler if you just take a generic closure directly:

struct FragMsgReceiver<R>
where
R: for<'b> FnMut(&'b mut [u8])
-> Box<MockFutureTrait<Item = &'b mut [u8]> + 'b>,
{
recv_dgram: R,
}

fn main() {
let fmr = FragMsgReceiver {
recv_dgram: |buf| Box::new(MockFuture { item: buf }),
};
}

why does guard break type inference?

TLDR; single line closure compilation is different from multi line

Long answer: Let’s forget single line closures for sometime. When we write generic function accepting a generic as argument type and then call this function, compiler needs to know at time of invocation about what’s the type of function needed i.e. the type of its argument. Now consider this argument is a closure. Again compiler needs to know what’s the type of closure (function signature) at time of invocation. This info should be available in the signature of the closure (i.e. argument and return type), compiler doesn’t worry (and rightly so) about the body of the closure. So Service A is behaving perfectly as expected from compiler i.e. the closure signature doesn’t give any clue of its type.

Now comes in single line closure. Swift compiler has a built in type inference optimisation for single line closures only. When you write single line closure, compiler first kicks in its type inference and try to figure out about the closure including its return type etc from its only single line of body . This type inference kicks in for single line closures (with or without the context of generics).
This type inference is the reason that your service B works

So I’ll rephrase the question as “Why does the type inference works for single line closures?” because that’s an extra feature provided by Swift for single line closures only. As soon as you move to multi line closure (its not guard statement specific, same will happen if you put print(“hello world”) as well), this type inference is no more available. Though there may be other type inference checks available for multi line closures.

What you can do is just provide the Type info in closure signature
i.e (result: Response< Model >)

Swift Generics Type Inference Extensions

When you define max (and min) like this:

func max<T, U : Comparable>(f: T -> U ) -> U?

You're actually saying that the closure f might take a different type than the elements in the Array. Since Array is already a generic struct Array<T>, you can re-use the element type T that it's already defined. From the Swift language guide:

When you extend a generic type, you do not provide a type parameter
list as part of the extension’s definition. Instead, the type
parameter list from the original type definition is available within
the body of the extension, and the original type parameter names are
used to refer to the type parameters from the original definition.

So, since the element type T is already available to you, simply take the T out of the type parameter list, like so:

func max<U : Comparable>(f: T -> U ) -> U?

That guarantees that the closure f will take the same type T that the elements in the Array have. Then the compiler can correctly infer which types you are using in your test cases and fixes the errors you're seeing.



Related Topics



Leave a reply



Submit