Compiler Error when using Either from dartz

1k views Asked by At

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

2

There are 2 answers

2
lrn On BEST ANSWER

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

0
Silverdust On

I finally figured out whats happening by diving into the types of dartz. The problem is that the compiler is incapable of infering the type of Either in contexts where only either Left or Right is being used. I.e. Left('') the compiler can infer the Left part of Either as being as string and in Right(5) its capable of inferring the right part of Either as int. He is incapable however to figure out the other part respectively. Using the code below works as indended.

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<String, int>('are you already questioning the meaning of your life?');
});