dartstreameitherdartz

Compiler Error when using Either from dartz


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


Solution

  • 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).