【问题标题】:Type inference behaves differently for similar cases对于类似情况,类型推断的行为不同
【发布时间】:2023-04-01 23:39:01
【问题描述】:

运行以下代码 (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 的类型推断算法使这些情况的行为不同的原因是什么?似乎函数 foobar 的类型应该很容易推断,因为它们总是返回相同的值。我也不清楚为什么在这些情况下移动函数声明的位置会改变类型推断。

有人知道这里发生了什么吗?

【问题讨论】:

    标签: dart type-inference


    【解决方案1】:

    Leaf Petersen 在dart-lang/sdk issue #33137: Type inference of function return value 的评论中解释了这一点:

    这是设计使然。我们确实推断出非递归 local 的返回类型 函数(在另一个函数范围内声明的函数 或方法),但对于顶级函数和方法,我们不推断 返回类型(通过覆盖推断除外)。原因如下 如下:

    • 方法和顶级函数通常是程序 API 的一部分,能够快速读取程序的 API 是很有价值的 一段代码。做基于方法体的返回类型推断手段 理解 API 的签名需要通读 方法体。
    • 方法和顶级函数可以任意相互递归,这使得推理问题变得更加困难和更多 很贵。

    主要出于这些原因,我们不会推断顶级函数和方法的返回类型。省略返回类型只是dynamic 的另一种说法。

    如果你设置了

    analyzer:
      strong-mode:
        implicit-dynamic: false
    

    在您的analysis_options.yaml 文件中,当顶级函数具有隐式dynamic 返回类型时,dartanalyzer 将生成错误:

      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
    

    【讨论】:

      【解决方案2】:

      看起来嵌套函数的处理方式与顶级函数不同。 这可能是一个错误。我在 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

      这是预期的行为。 局部函数使用类型推断来推断其返回类型,但顶级/类级函数不使用。

      区分的主要原因是顶级和类级别函数与类型声明存在于同一级别。如果我们还必须在我们甚至不知道类的签名的时候分析函数体,那么解决类型和函数之间的循环依赖就会变得更加困难。

      当顶层推断完成时,我们确实知道类型层次结构,并且顶层函数是无序的,它们可以以任意方式相互引用,局部函数是线性的,只能依赖于全局函数或先验局部函数。这意味着我们可以在本地分析函数体以找到返回类型,而无需查看函数体本身以及我们已经分析过的东西之外的任何东西。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-06-01
        • 2019-05-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-05
        • 1970-01-01
        相关资源
        最近更新 更多