Closed kika closed 5 years ago
Here is a good litmus test for subtyping: if A
is a subtype of B
then you can use A
everywhere where you can use B
. (see Liskov's Substitution Principle).
For example if Dog
is a subtype of Animal
you can use Dog
anywhere where you can use Animal
.
Now consider (BuildContext, String) => ListTile
and (BuildContext, dynamic) => Widget
: the first function only takes String
as a parameter, while the other takes anything dynamic
. Which means that you can't use the first function everywhere where the second function is used. Consider the code like this:
void Function(String) f;
void Function(dynamic) g;
g(10); // valid
f(10); // invalid, which means f can't be used like g
Formally speaking function types are contravariant with respect to their parameter types and covariant with respect to return types.
@mraleph Exactly. I can use String
everywhere I can use dynamic
. I can use ListTile
everywhere I can use Widget
, not the other way around (in other words, I can pass ListTile
to a function which promises to operate on any Widget
, but I can't pass any Widget
to a function which promises to operate on ListTile
only).
Your example is correct but illustrates a different issue: you have three types there - int
, String
and dynamic
. int
and String
are siblings in the ancestry tree of sub/super types and you can't use them interchangeably.
Which means that you can't use the first function everywhere where the second function is used.
God forbid, why?!? The second function promises to take any dynamic
and produce a Widget
. We give her a String
and ask for a ListTile
. We are talking about type signatures here, not implementations, correct? LSP has some wording around could be safely used
which allows different interpretations, but this issue is clearly about typechecker. You can't technically write an implementation of a function that returns Widget
in Flutter, because Widget
is abstract. (well, I believe, don't hold me accountable for this).
https://en.wikipedia.org/wiki/Subtyping
https://en.wikipedia.org/wiki/Subtyping#/media/File:Inheritance.svg
(I cringe at the word "inheritance" in the title, but it is what it is).
I'm saying that ListTile
is a duck
and Widget
is a bird
. You say it's the other way around. Someone help me :-)
Which means that you can't use the first function everywhere where the second function is used.
God forbid, why?!? The second function promises to take any
dynamic
and produce aWidget
. We give her aString
and ask for aListTile
.
You're trying to use the second function where the first is used, which is the opposite. (That doesn't work either, because you expect a ListTile
and you're getting a Widget
instead.)
The reason you can't use the first function everywhere that the second function is used is: the first function promises to take any String
and produce a ListTile
. You give her a dynamic
and oops.
@kika
let me rewrite my example to make it clear:
void useSomeFunction(void Function(dynamic) g) {
g(10);
g(Cat());
g(Dog());
g(SpaceShip());
}
var f = (String value) { /* I can only handle String inputs */ };
useSomeFunction(f); // Invalid.
// Can't pass a function that can only deal with String inputs
// somewhere where a function that can deal with *any* inputs
// is expected.
@mraleph @kmillikin Thanks!
Indeed A ≤ B doesn't mean that f(A) ≤ f(B), but I believe that the error message could be enhanced. In its current form, it will come back biting many times, especially given the more "popular" error message List<dynamic> is not a subtype of List<Whatever>
when you forget to use List.from(...)
.
Dart VM version: 2.1.1-dev.0.1.flutter-2cb346bd0c (Tue Jan 8 15:52:54 2019 +0000) on "macos_x64"
onMacOS 10.13.6
I fail to comprehend a simple error message from Dart:
(BuildContext, String) => ListTile' is not a subtype of type '(BuildContext, dynamic) => Widget
How this is true?
String
is the subtype ofdynamic
, everything is, right?dynamic
is a subtype and a supertype at the same time.ListTile
is the subtype ofWidget
, what else? How come this message could be true?Context: https://github.com/AbdulRahmanAlHamali/flutter_typeahead/issues/23