I am currently experimenting with the flutter framework and dart and stumbled across a seemingly strange behaviour I fail to understand. Even though the context in which the actual problem occurs is way more complicated, I was even able to replicate it in an extremely simplified showcase:
Stream<Either<String, int>> result1 = Stream.fromIterable([1, 2, 3, 4, 5])
.map((number) => number < 4 ? Right(1) : Left('error'))
.onErrorReturnWith((error) => Left('error'));
While the sample above compiles uncontradicted, I do get a compile error for the sample below:
Error: A value of type 'Left<String, dynamic>' can't be assigned to a variable of type 'Right<dynamic, int>'
Stream<Either<String, int>> result2 = Stream.fromIterable([1, 2, 3, 4, 5])
.map((number) => Right(1))
.onErrorReturnWith((error) => Left('error'));
Is anyone capable to shed some light to this manner?
########################################################
Another example:
Future<Either<String, int>> someWebCall() {
Future<int> response = Future.delayed(Duration(milliseconds: 200), () {
throw SocketException('Well... ...it happens you know...');
});
return response.then((number) {
return number > 50.0 ? Right(number) : Left('number is too small...');
}).catchError((error) {
return Left('are you already questioning the meaning of your life?');
});
}
This compiles but ends with a runtime error: type 'Future' is not a subtype of type 'Future<Either<String, int>>'
Then I tried to give as many hints to the compiler as I could coming up with this approach:
Future<Either<String, int>> someWebCall() {
Future<int> response = Future.delayed(Duration(milliseconds: 200), () {
throw SocketException('Well... ...it happens you know...');
});
return response.then<Either<String, int>>((number) {
return number > 50.0 ? Right(number) : Left('number is too small...') as Either<String, int>;
}).catchError((error) {
return Left('are you already questioning the meaning of your life?') as Either<String, int>;
});
}
Now I am getting: type 'Left<String, dynamic>' is not a subtype of type 'Either<String, int>' in type cast
I really can't wrap my head around this
The type of the function (number) => Right(1)
is Right<dynamic, int> Function(int)
, which means that the resulting stream of the map
call is a Stream<Right<dynamic, int>>
.
The onErrorReturnWith
needs to return something of the same type as the elements of the stream it's called on, but it returns Left<String, dynamic>
, not Right<dynamic, int>
.
The simplest fix is to tell the map
call what type to return:
...
.map<Either<String, int>>( .... )
Then the types should be what you expect (and not Either<dynamic, dynamic>
like the first example likely inferred).