hironytic / BFTaskPromise

An Objective-C category for BFTask class in Bolts-iOS
Other
11 stars 4 forks source link

Add Generic Support for callbacks #8

Open felix-dumit opened 8 years ago

felix-dumit commented 8 years ago

With the new update to Bolts we can do something like this:

BFTask<NSString*> *task = [BFTask taskWithResult:@"hello"];

Can we add support so that when we call the .then method instead of id we correctly infer the parameter type of result:

task.then(^id(NSString* result){
   //
});
hironytic commented 8 years ago

@felix-dumit Thank you for letting me know. I've never noticed on Bolts' update.

I'm not familiar with new generics feature with Objective-C. IIRC, specifying concrete type instead of id doesn't cause any errors or warnings on Xcode 6.4, on which generics is not supported. Now on Xcode 7 with generics, are there any problems?

felix-dumit commented 8 years ago

No there are no issues. I just wanted to see if we could find a solution to automatically infer the parameter type of the result.

hironytic commented 8 years ago

I would like to make it clear that we should do about this. Are you saying about autocomplete/snippet related things? If so, I have no idea at this time.

Anyway, I'll give it a try to use Bolts with generics. Let me have a time.

felix-dumit commented 8 years ago

Basically it relates to the block parameter. Currently it is as id and we can cast to anything we want, but it's not as safe as if the block parameter was of the generic type.

I haven't played much with generics in Bolts as well so I'll also be taking a look.

hironytic commented 8 years ago

Ah, I've almost understood. When I write a code like:

BFTask<NSString*> *task = [BFTask taskWithResult:@"hello"];
task.then(^id(NSNumber* result){  // type mismatch!
   //
});

it is nice that an error or a warning is reported.

hironytic commented 8 years ago

I took a little try Bolts with generics. (pure Bolts, not used with our extensions)

In Xcode 7, the code below produces no errors/warnings (expected):

BFTask<NSString *> *task = [BFTask taskWithResult:@"Hey"];
[task continueWithBlock:^id(BFTask<NSString *> *task) {
  // ...
}];

And this produces no errors/warnings, too (unexpected):

BFTask<NSString *> *task = [BFTask taskWithResult:@"Hey"];
[task continueWithBlock:^id(BFTask<NSNumber *> *task) {
  // ...
}];

continueWithBlock: takes a block whose type is BFContinuationBlock. BFContinuationBlock is defined here. After expanding BF_GENERIC macro, it should become like this.

@interface BFTask <__covariant BFGenericType> : NSObject
typedef id(^BFContinuationBlock)(BFTask <BFGenericType> *task);
// ...
@end

Is the type of block parameter not checked? I don't know much about Objective-C generics. :confused:

hironytic commented 8 years ago

By the way, I found that if type parameter was strictly checked, chaining the block had a problem. continueWithBlock returns instancetype. So same BFGenericType type parameter should be used. The code below uses unmatched type because the first continueWithBlock returns BFTask<NSString *>.

BFTask<NSString *> *task = [BFTask taskWithResult:@"Hey"];
[[task continueWithBlock:^id(BFTask<NSString *> *task) {
    // returns NSNumber
    return @100;
}] continueWithBlock:^id(BFTask<NSNumber *> *task) {
    // ...
}];

I took a look at Bolts-Java, two type parameters were used there.

public class Task<TResult> {
  // ...
  public <TContinuationResult> Task<TContinuationResult> continueWith(
    Continuation<TResult, TContinuationResult> continuation) {
        // ...
  }
  // ...
}

What's the best way :disappointed_relieved:

felix-dumit commented 8 years ago

I tried to play around with it and add some BF_GENERIC to our methods but without any luck. I guess we keep it as is then