对于类似情况,类型推断的行为不同
Type inference behaves differently for similar cases
运行 下面的代码(Dart 2.3)抛出异常:
type 'List<dynamic>' is not a subtype of type 'List<bool>'
bar() => 0;
foo() => [bar()];
main() {
var l = [1, 2, 3];
l = foo();
}
然而,这个稍作改动的示例运行正确:
main() {
bar() => 0;
var l = [1, 2, 3];
l = [bar()];
}
这样做:
main() {
bar() => 0;
foo() => [bar()];
var l = [1, 2, 3];
l = foo();
}
使这些情况表现不同的 Dart 类型推断算法是什么?似乎 foo
和 bar
函数的类型应该很容易推断出来,因为它们总是 return 相同的值。我也不清楚为什么在这些情况下移动函数声明的位置会改变类型推断。
有人知道这是怎么回事吗?
看起来嵌套函数的处理方式与 top-level 函数不同。 这可能是一个错误。 我在 Dart 2.3.1 上从 Dartpad 得到以下信息。
foo() => 0;
bar() => [foo()];
main() {
baz() => 0;
qux() => [baz()];
print(foo.runtimeType);
print(bar.runtimeType);
print(baz.runtimeType);
print(qux.runtimeType);
}
// () => dynamic
// () => dynamic
// () => int
// () => List<int>
解释here:
This is expected behavior.
Local functions use type inference to deduce their return type, but top-level/class-level functions do not.
The primary reason for the distinction is that top-level and class level functions exist at the same level as type declarations. Solving cyclic dependencies between types and functions gets even harder if we have to also analyze function bodies at a time where we don't even know the signature of classes yet.
When top-level inference has completed, we do know the type hierarchies, and where top-level functions are unordered, they can refer to each other in arbitrary ways, local functions are linear and can only depend on global functions or prior local functions. That means that we can analyze the function body locally to find the return type, without needing to look at anything except the body itself, and things we have already analyzed.
Leaf Petersen 在对 dart-lang/sdk issue #33137: Type inference of function return value 的评论中对此进行了解释:
This is by design. We do infer return types of non-recursive local
functions (functions declared inside of the scope of another function
or method), but for top level functions and methods, we do not infer
return types (except via override inference). The reasons are as
follows:
- Methods and top level functions are usually part of the API of a program, and it's valuable to be able to quickly read off the API of a
piece of code. Doing method body based return type inference means
that understanding the signature of the API requires reading through
the method body.
- Methods and top level functions can be arbitrarily mutually recursive, which makes the inference problem much harder and more
expensive.
For primarily these reasons, we do not infer return types for top level functions and methods. Leaving off the return type is just another way of saying dynamic
.
如果你设置
analyzer:
strong-mode:
implicit-dynamic: false
在您的 analysis_options.yaml
文件中,然后 dartanalyzer
将在 top-level 函数具有隐式 dynamic
return 类型时生成错误:
error • Missing return type for 'bar' at example.dart:1:1 • strong_mode_implicit_dynamic_return
error • Missing return type for 'foo' at example.dart:2:1 • strong_mode_implicit_dynamic_return
运行 下面的代码(Dart 2.3)抛出异常:
type 'List<dynamic>' is not a subtype of type 'List<bool>'
bar() => 0;
foo() => [bar()];
main() {
var l = [1, 2, 3];
l = foo();
}
然而,这个稍作改动的示例运行正确:
main() {
bar() => 0;
var l = [1, 2, 3];
l = [bar()];
}
这样做:
main() {
bar() => 0;
foo() => [bar()];
var l = [1, 2, 3];
l = foo();
}
使这些情况表现不同的 Dart 类型推断算法是什么?似乎 foo
和 bar
函数的类型应该很容易推断出来,因为它们总是 return 相同的值。我也不清楚为什么在这些情况下移动函数声明的位置会改变类型推断。
有人知道这是怎么回事吗?
看起来嵌套函数的处理方式与 top-level 函数不同。 这可能是一个错误。 我在 Dart 2.3.1 上从 Dartpad 得到以下信息。
foo() => 0;
bar() => [foo()];
main() {
baz() => 0;
qux() => [baz()];
print(foo.runtimeType);
print(bar.runtimeType);
print(baz.runtimeType);
print(qux.runtimeType);
}
// () => dynamic
// () => dynamic
// () => int
// () => List<int>
解释here:
This is expected behavior. Local functions use type inference to deduce their return type, but top-level/class-level functions do not.
The primary reason for the distinction is that top-level and class level functions exist at the same level as type declarations. Solving cyclic dependencies between types and functions gets even harder if we have to also analyze function bodies at a time where we don't even know the signature of classes yet.
When top-level inference has completed, we do know the type hierarchies, and where top-level functions are unordered, they can refer to each other in arbitrary ways, local functions are linear and can only depend on global functions or prior local functions. That means that we can analyze the function body locally to find the return type, without needing to look at anything except the body itself, and things we have already analyzed.
Leaf Petersen 在对 dart-lang/sdk issue #33137: Type inference of function return value 的评论中对此进行了解释:
This is by design. We do infer return types of non-recursive local functions (functions declared inside of the scope of another function or method), but for top level functions and methods, we do not infer return types (except via override inference). The reasons are as follows:
- Methods and top level functions are usually part of the API of a program, and it's valuable to be able to quickly read off the API of a piece of code. Doing method body based return type inference means that understanding the signature of the API requires reading through the method body.
- Methods and top level functions can be arbitrarily mutually recursive, which makes the inference problem much harder and more expensive.
For primarily these reasons, we do not infer return types for top level functions and methods. Leaving off the return type is just another way of saying
dynamic
.
如果你设置
analyzer:
strong-mode:
implicit-dynamic: false
在您的 analysis_options.yaml
文件中,然后 dartanalyzer
将在 top-level 函数具有隐式 dynamic
return 类型时生成错误:
error • Missing return type for 'bar' at example.dart:1:1 • strong_mode_implicit_dynamic_return
error • Missing return type for 'foo' at example.dart:2:1 • strong_mode_implicit_dynamic_return