Sdk: Type error in function invocation (generic type is inferred to Null instead of actual type)

Created on 30 Aug 2020  路  4Comments  路  Source: dart-lang/sdk

$ dart --version
Dart SDK version: 2.10.0-7.2.beta (beta) (Mon Aug 17 11:01:01 2020 +0200) on "windows_x64"

Expected output

Some(foo)

Actual output

Unhandled exception:
type '() => Option<String>' is not a subtype of type '() => Option<Null>' of 'ifNone'
#0      Option.orElse (package:zowo_lib/option_bug.dart)
#1      main (package:zowo_lib/option_bug.dart:27:27)
#2      _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:301:19)
#3      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)

Repro code

import 'dart:io';

class Option<A> {
  final A unsafeGet;

  const Option.some(this.unsafeGet);
  const Option.none() : unsafeGet = null;
  @override String toString() => isSome ? "Some($unsafeGet)" : "None";

  bool get isSome => unsafeGet != null;

  Option<A> orElse(Option<A> Function() ifNone) => isSome ? this : ifNone();
}

Option<A> Some<A>(A a) => Option.some(a);

extension MapExts<K, V> on Map<K, V> {
  Option<V> get(K key) => containsKey(key) ? Some(this[key]) : const Option.none();
}

final map = {"foo": "bar"};

void main() {
  final noneStr = map.get("baz");
  final someStr = noneStr.orElse(() => map.get("foo"));
  stdout.write(someStr);
}

Most helpful comment

Though I would still say that it is highly unintuitive

Agreed, this is a tricky error to spot (even for very experienced Dart developers, see https://github.com/google/quiver-dart/commit/41b4f6937d566228178b609fb0c8c7724535f583 for instance).

seems like it is leaking implementation details of the VM into the userspace code

This is not the VM, it's the Dart compiler behaving like specified. You'll get the same result with dart2js and ddc.

It's just that Option<Null> is assignable to Option<V>

Time to :+1: https://github.com/dart-lang/language/issues/213 then :)

All 4 comments

The problem is const Option.none(). Since it's constant, it can't use the V type parameter which would depend on the map you're passing to get. In your case, the type parameter of const Option.none() is inferred to be Null. You can verify this by printing noneStr.runtimeType - it's Option<Null>.

Your function has a type of Option<String> Function(), which is not a subtype of the Option<Null> Function() that would be expected by Option<Null>.orElse(). So, you're getting a type error at runtime. You can fix this by simply removing the const keyword in MapExts.

Thanks for clarifying.

Though I would still say that it is highly unintuitive and seems like it is leaking implementation details of the VM into the userspace code.

It is very confusing when the code compiles and says "yup, this will be Option<A>, no worries" and fails in runtime. I guess one can reason about it once you know how Dart does things under the hood. Personally I would rather emit a compilation error in the get function telling me that const isn't allowed here.

Turns out it does complain if you specify type arguments explicitly...

image

It's just that Option<Null> is assignable to Option<V>. Which according to https://dart.dev/faq#q-why-are-generics-covariant is "reasonable" (to which I disagree). Case closed I guess.

Thanks for the help!

Though I would still say that it is highly unintuitive

Agreed, this is a tricky error to spot (even for very experienced Dart developers, see https://github.com/google/quiver-dart/commit/41b4f6937d566228178b609fb0c8c7724535f583 for instance).

seems like it is leaking implementation details of the VM into the userspace code

This is not the VM, it's the Dart compiler behaving like specified. You'll get the same result with dart2js and ddc.

It's just that Option<Null> is assignable to Option<V>

Time to :+1: https://github.com/dart-lang/language/issues/213 then :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nex3 picture nex3  路  3Comments

xster picture xster  路  3Comments

gspencergoog picture gspencergoog  路  3Comments

DartBot picture DartBot  路  3Comments

DartBot picture DartBot  路  3Comments