You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
My understanding of the concept of djinnis Future/Promise implementation is that a future will always result in either a value of the given type or an exception. The get method waits for a result to be present and then either returns the value or throws the exception. So by the time get returns, a value must be present.
This is already the case for the Cpp implementation of Future/Promise. If I want to have a nullable result, I must use Future<std::optional<int>>.
The ObjC implementation allows calls to setValue with a nullable value, regardless of the value type. Therefore get also needs to return a nullable value. I would love to change this, because it doesn't match the implementation in other platforms and goes against my understanding of the concept. It also makes usage from Swift harder/weirder, because we always need to unwrap a value that is expected to be always present.
Possible solution
I would most like to simply change DJFuture::get and DJPromise::setValue to return and accept nonnull values.
However, I'm guessing the value was originally made nullable for a reason. Namely: what if the user wants the future to return something nullable? In C++ this is easy to achieve with Future<optional<...>>. I lack ObjC knowledge here, but I'm guessing there is no way to dictate nullability via template arg in ObjC, which is why the value was made nullable by default. Is this true? Is there no other way?
Other observations
DJPromise::setValue without parameters sets a different kind of null than DJPromise::setValue:nil
Marshalling between ObjC and C++ currently crashes for types that C++ expects to be non-nullable, if ObjC sets a nil value
I've created this PR in my fork to showcase these issues, see the breaking tests in the CI. For reference, I'll copy the test code and CI output here as well:
Test code
- (void)testFutureObjCNullable {
DJPromise<NSNumber *> *p1 = [[DJPromise alloc] init];
DJFuture<NSNumber *> *f1 = [p1 getFuture];
[p1 setValue:nil];
DJPromise<NSNumber *> *p2 = [[DJPromise alloc] init];
DJFuture<NSNumber *> *f2 = [p2 getFuture];
[p2 setValue];
// setValue:nil doesn't set the same value as setValue without parameters.// Is this expected? Is it good practice to use [NSNull null], like setValue does?// It seems problematic to me, but I lack the ObjC knowledge.XCTAssertEqual([f1 get], [f2 get]);
}
- (void)testFutureRoundtripWithNil {
// This shows a crash that occurs when passing nil into a promise that, on cpp side, doesn't expect nil values
DJPromise<NSNumber *> *p = [[DJPromise alloc] init];
DJFuture<NSNumber *> *f = [p getFuture];
DJFuture<NSString *> *f2 = [DBTestHelpers futureRoundtrip:f];
[p setValue:nil];
}
- (void)testFutureRoundtripWithNSNull {
// This shows a crash that occurs when passing [NSNull null] into a promise that, on cpp side, doesn't expect nil values
DJPromise<NSNumber *> *p = [[DJPromise alloc] init];
DJFuture<NSNumber *> *f = [p getFuture];
DJFuture<NSString *> *f2 = [DBTestHelpers futureRoundtrip:f];
[p setValue];
}
One more question: is there any way to prevent calling setValue without parameter for promises with non-optional values? Or to make setValue take a nullable parameter if and only if the promises value is optional? DJPromise<nonnull Foo *> doesn't compile and I'm not sure how powerful ObjC generics are in this regard 🤔
What is the problem
My understanding of the concept of djinnis Future/Promise implementation is that a future will always result in either a value of the given type or an exception. The
get
method waits for a result to be present and then either returns the value or throws the exception. So by the timeget
returns, a value must be present.This is already the case for the Cpp implementation of Future/Promise. If I want to have a nullable result, I must use
Future<std::optional<int>>
.The ObjC implementation allows calls to
setValue
with a nullable value, regardless of the value type. Thereforeget
also needs to return a nullable value. I would love to change this, because it doesn't match the implementation in other platforms and goes against my understanding of the concept. It also makes usage from Swift harder/weirder, because we always need to unwrap a value that is expected to be always present.Possible solution
I would most like to simply change
DJFuture::get
andDJPromise::setValue
to return and acceptnonnull
values.However, I'm guessing the value was originally made nullable for a reason. Namely: what if the user wants the future to return something nullable? In C++ this is easy to achieve with
Future<optional<...>>
. I lack ObjC knowledge here, but I'm guessing there is no way to dictate nullability via template arg in ObjC, which is why the value was madenullable
by default. Is this true? Is there no other way?Other observations
DJPromise::setValue
without parameters sets a different kind of null thanDJPromise::setValue:nil
nil
valueI've created this PR in my fork to showcase these issues, see the breaking tests in the CI. For reference, I'll copy the test code and CI output here as well:
Test code
CI output
The text was updated successfully, but these errors were encountered: