Propagation of inner type with Future with Dart 2

Dart VM version: 2.0.0-dev.67.0 (Tue Jul 3 18:17:07 2018 +0200) on “macos_x64”

Let’s assume following code.

import 'dart:async';

class Zoo {
  Map<String, Animal> animals = <String, Animal>{};

  void add(Animal animal) => animals[animal.name] = animal;

  Animal get(String name) => animals[name];
  Cat getCat(String name) => get(name);
  Dog getDog(String name) => get(name);

  Future<Animal> asyncGet(String name) async => await new Future<Animal>.value(animals[name]);
  Future<Cat> asyncGetCat(String name) async => await asyncGet(name);
  Future<Dog> asyncGetDog(String name) async => await asyncGet(name);

  Future<Cat> badAsyncGetCat(String name) => asyncGet(name);
  Future<Dog> badAsyncGetDog(String name) => asyncGet(name);

  String toString() => "Zoo: $animals";
}

class Animal {
  String name;
  String toString() => "$runtimeType: $name";
}

class Cat extends Animal {
  void meow() => print("$name: Meow !");
}

class Dog extends Animal {
  void waouf() => print("$name: Waouf !");
}

Future<Null> main() async {
  var zoo = new Zoo()..add(new Animal()..name = 'Foo')..add(new Cat()..name = 'Felix')..add(new Dog()..name = 'Brutus');
  print(zoo);

  // Sync zoo
  {
    var animal = zoo.get('Felix');
    print(animal.runtimeType);

    var cat = zoo.getCat('Felix');
    print(cat.runtimeType);

    var dog = zoo.getDog('Brutus');
    print(dog.runtimeType);

    var nobody = zoo.getCat('Nobody');
    print(nobody.runtimeType);
  }

  // Async zoo
  {
    var animal = await zoo.asyncGet('Felix');
    print(animal.runtimeType);

    var cat = await zoo.asyncGetCat('Felix');
    print(cat.runtimeType);

    var dog = await zoo.asyncGetDog('Brutus');
    print(dog.runtimeType);

    var nobody = await zoo.asyncGetCat('Nobody');
    print(nobody.runtimeType);
  }

  // Bad async zoo
  {
    var cat = await zoo.badAsyncGetCat('Felix');
    print(cat.runtimeType);

    var dog = await zoo.badAsyncGetDog('Brutus');
    print(dog.runtimeType);

    var nobody = await zoo.badAsyncGetCat('Nobody');
    print(nobody.runtimeType);
  }
}

The output result is following.

$ dart animal.dart
Zoo: {Foo: Animal: Foo, Felix: Cat: Felix, Brutus: Dog: Brutus}
Cat
Cat
Dog
Null
Cat
Cat
Dog
Null
Cat
Unhandled exception:
type 'Future<Animal>' is not a subtype of type 'Future<Cat>'
#0      Zoo.badAsyncGetCat (async.dart:17:46)
#1      main (file:///async.dart:75:25)
#2      _RootZone.runUnary (dart:async/zone.dart:1381:54)
#3      _FutureListener.handleValue (dart:async/future_impl.dart:129:18)
#4      Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:638:45)
#5      Future._propagateToListeners (dart:async/future_impl.dart:667:32)
#6      Future._completeWithValue (dart:async/future_impl.dart:482:5)
#7      Future._asyncComplete.<anonymous closure> (dart:async/future_impl.dart:512:7)
#8      _microtaskLoop (dart:async/schedule_microtask.dart:41:21)
#9      _startMicrotaskLoop (dart:async/schedule_microtask.dart:50:5)
#10     _runPendingImmediateCallback (dart:isolate/runtime/libisolate_patch.dart:113:13)
#11     _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:166:5)

I understand well the thrown exception.

I would like to raise the point, because I found the “workaround” with async/await that sounds weird.

So, as Future is a kind of special type, I suggest that the inner type propagation should be allowed for Future.

What do you think about it ?

Author: Fantashit

1 thought on “Propagation of inner type with Future with Dart 2

  1. It seems silly to specialize this case. I wouldn’t expect, for example:

    class Animal {}
    class Cat extends Animal {}
    
    Future<Animal> getAnimal() => new Future.value(new Cat());
    
    main() {
      Future<Cat> cat = getAnimal();
    }

    … to succeed, so specializing it for async functions is just asking for confused users to try inserting async everywhere` until it does.

Comments are closed.