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)")
})
}
}
}
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:
- Declare
isFavorite
variable on theLandmark
type. - Make sure you update the
landmarkData.json
for every landmark record with theisFavorite = 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
Passing Dynamic Int Variable from One Class to Another Class in Swift
Any or a Trouble with Sequence of Functions
Easiest Way to Increment a Data Point in Firebase
Write into Settings Bundle in Swift
Making Sklabelnode as a Crop Node of Skshapenode
How to Use a Protocol with Optional Class Methods in an Extension with Generic in Swift
Custom Uitabbar Unselected Item's Color
Swift: No Idea How to Get Back the Selected Value from a Popover to the Calling Controller
Swift Protocol as Generic Parameter
Tvos Remote Notification Replacement
Calling Stop() on Avaudioplayernode After Finished Playing Causes Crash
Gcdasyncsocket Multiple Connections Wont Accept Data from Multiple Sockets
How to Implement a Generic Struct That Manages Key-Value Pairs for Userdefaults in Swift
Why Does This Swiftui Picker Code Not Work
Swiftui Multiline Text Background Color