Open duck-dev-go opened 1 year ago
I think the more idiomatic solution to the SO question would use map
. Something like:
Future<Either<Failure, AuthUser>> call() async {
final userResponse = await _authRepository.getUser();
return userResponse.flatMap((user) {
final accessTokenExpiresAtMilliseconds =
user.accessTokenIssuedAtMilliseconds +
user.accessTokenExpiresInMilliseconds;
final accessTokenExpiresAtDateTime =
DateTime.fromMillisecondsSinceEpoch(accessTokenExpiresAtMilliseconds);
if (DateTime.now().isBefore(accessTokenExpiresAtDateTime)) {
return right(user);
}
return _authRepository.refreshUser(user: user);
});
}
This makes some assumptions on the signatures of the AuthRepository
methods, but the general idea should hold. This uses the basic Either
combinators and type system instead of having to rely on an error prone cast.
The map is what I used before but you can get deeply nested by doing that if the logic is just a bit more complex. Other than that I don't think there is an async map either so it doesn't seem to work well with async functions.
I understand that the either promotes handling all errors. But in certain situation I think you should be able to deviate from it so that the code doesn't become unnecessarily complex.
For async behavior, I would recommend using Task
instead of Future
. To combat complexity/nesting, break parts into smaller pieces and then use the existing combinators on Either
, Task
, etc. to build up your program to the desired functionality.
Is there any example how to use combinators? I'm assuming this would work also for 2 eithers the have different types for right? How would one combine 2 eithers and fold it?
Sure a simple example:
final foo = right<String, int>(42);
final bar = right<String, String>("duck");
final combined = Either.map2(foo, bar, (a, b) => 'happy $a birthday $b!!');
final folded = combined.fold((l) => 'error: $l', (r) => r);
Hi thanks for the example. I'm still however a bit confused. In my example I have 2 futures that I work with. The first one needs to resolve because I need it's result for the second one. Your example is not for async operations it seems. So you suggest using a Task right? I'm not seeing myself how a task would solve this problem.
So the code below would work with the extension. But how can I make this same code work with the api provided by this package? A map won't work since we are working with a future here.
Future<Either<Failure, SomeValue1>> doSomething1() {
// return left and right
}
Future<Either<Failure, SomeValue2>> doSomething2(SomeValue1 input) {
// return left and right
}
Future<Either<Failure, SomeValue2>> combine() {
final result = await doSomething1();
if(result.isLeft()){
return result.asLeft();
}
return doSomething2(result.asRight());
}
Yes you're correct that in your case Task
alone won't solve your problem. It can get a tad more messy when you're working with nested Monads, Task
and Either
in this case, but this pattern should solve your issue:
// Your original Future-based functions
Future<Either<Failure, SomeValue1>> doSomething1Fut() =>
throw UnimplementedError();
Future<Either<Failure, SomeValue2>> doSomething2Fut() =>
throw UnimplementedError();
// Dartz Task functions
Task<Either<Failure, SomeValue1>> doSomething1() =>
Task(() => doSomething1Fut());
Task<Either<Failure, SomeValue2>> doSomething2() =>
Task(() => doSomething2Fut());
// Using flatMap to sequence the Tasks, map2 to combine the Eithers
Task<Either<Failure, SomeValue3>> combine() {
return doSomething1().flatMap((value1) {
return doSomething2().map((value2) {
return Either.map2(value1, value2,
(a, b) => throw UnimplementedError('combine the values here'));
});
});
}
For me a scenario like this comes up a lot. For example when you are working with remote and local datasources and you want to write to the cache or disk in-between. I can imagine that the solution I proposed doesn't align with the goals of this project. But I do think ideally there should be a more streamlined solution for this supported by the package itself.
In the following scenario
https://stackoverflow.com/questions/75007868/with-dartz-is-it-possible-to-pass-a-failure-on-to-the-function-that-is-folding-m/75008089#75008089
it would be great to have an asLeft and asRight as was proposed in the answer.
Inside this, you may like to access data directly, this extension will help