【问题标题】:Why can't Dart infer the type of List.fold()?为什么 Dart 不能推断 List.fold() 的类型?
【发布时间】:2019-01-26 14:56:53
【问题描述】:

我正在 Dart 2.1.0 中尝试List.fold()

void main() {
  final a = [1,2,3];
  print(a.fold(0, (x,y) => x + y));
}

返回一个错误:

错误:没有为类“dart.core::Object”定义方法“+”。

尝试将名称更正为现有方法的名称,或定义一个名为“+”的方法。

似乎无法推断x的类型为int,所以不知道如何在其中应用+

根据method specx的类型应该与初始值0相同,显然是int为什么 Dart 不能推断?

我可以通过显式提示类型来使其工作:

void main() {
  final a = [1,2,3];
  print(a.fold<int>(0, (x,y) => x + y));
}

但我有点失望,Dart 无法为我推断出这一点。在大多数其他情况下,它的类型推断似乎更强。

【问题讨论】:

    标签: dart flutter type-inference


    【解决方案1】:

    Dart 类型推断按预期工作:

    void main() {
      final a = [1,2,3];
    
      var sum = a.fold(0, (x,y) => x + y);
      print(sum);
    }
    

    在您的示例中失败的原因是因为类型推断结果取决于评估表达式的上下文:

    print(a.fold(0, (x,y) => x + y));
    

    抛出一个错误,因为print 参数应该是Object:推理使用Object 来填充T 类型参数的泛型fold

    T fold <T>(T initialValue, T combine(T previousValue, T element)) :
    

    验证这个假设:

    void main() {
      final a = [1,2,3];
    
      Object obj = a.fold(0, (x,y) => x + y);
    }
    

    抛出完全相同的错误。

    带走:

    类型推断效果很好,但必须注意泛型表达式的周围上下文。

    这是 Dart 的限制吗?

    我不认为这种行为是 dart 的限制,只是一种实现选择。

    可能还有一些我无法谈论的合理的理论原因,但我可以对此进行一些推理。

    考虑泛型方法:

    T fold <T>(T initialValue, T combine(T previousValue, T element))
    

    及其用法:

    Object obj = a.fold(0, (x,y) => x + y);
    

    推断T的类型有两种途径:

    1. fold 返回值分配给Object obj0Object,因为它是int,然后T“解析为”Object

    2. fold第一个参数是int,返回值应该是ObjectintObject, 然后T“解决”int

    Dart 选择路径 1:推断类型采用满足泛型方法的“最广泛”(超类型)。

    如果 Dart 实现路径 2 应该会更好(推断的类型是“最近”类型)?

    在这个特定的情况下可能是的,但是会有一些情况不适用于路径 2。

    例如,这个 sn-p 对路径 2 不满意:

    abstract class Sensor {
      String getType();
    }
    
    class CADPrototype extends Sensor {
      String getType() {
        return "Virtual";
      }
    }
    
    class Accelerometer extends Sensor {
      String getType() {
        return "Real";
      }
    }
    
    
    T foo<T extends Sensor>(T v1, T v2, T bar(T t1, T t2)) {
      if (v2 is CADPrototype) {
        return v1;
      }
      return v2;
    }
    
    Sensor foo_what_dart_does(Sensor v1, Sensor v2, Sensor bar(Sensor t1, Sensor t2)) {
      if (v2 is CADPrototype) {
        return v1;
      }
      return v2;
    }
    
    Accelerometer foo_infer_from_argument(Accelerometer v1, Accelerometer v2, Accelerometer bar(Accelerometer t1, Accelerometer t2)) {
      if (v2 is CADPrototype) {
        return v1;
      }
      return v2;
    }
    
    
    void main() {
      Accelerometer v1 = Accelerometer();
      CADPrototype v2 = CADPrototype();
    
      Sensor result;
      result = foo(v1, v2, (p1, p2) => p1);
    
      // it works
      result = foo_what_dart_does(v1, v2, (p1, p2) => p1);
    
      // Compilation Error: CADPrototype cannot be assigned to type Accelerometer
      result = foo_infer_from_argument(v1, v2, (p1, p2) => p1);
    
    }
    

    【讨论】:

    • 好吧,我还是有点失望。难道它不应该认识到intObject 的子类,并且在给定“上下文”的情况下,int 是一个更适合推断的类型?我明白你在说什么,但我仍然认为 Dart 的类型推断可能会更好。
    • 你的推理是有道理的。我更新了一些注意事项,希望对您有所帮助。
    • 您对fold 的规范有点偏离。 Fold 的规格应该是T fold &lt;T&gt;(T initialValue, T combine(T previousValue, E element))。请注意,元素类型可以不同。除此之外,我不太明白你在说什么,尽管我承认可能存在我不知道的理论问题。
    • 是的,为了这篇文章,我更喜欢简化 fold 的规范,因为我认为文档中的内容是错误的:据我所知,它应该是 T fold &lt;T, E&gt;(T initialValue, T combine(T previousValue, E element))
    • >Dart 类型推断按预期工作:老实说,在您的第一个示例中,推断不起作用,因为 a 的类型实际上是 dynamic,而实际上是“int”。它可以通过指定int a 的类型来修复。但无论如何,这不是预期的行为。
    猜你喜欢
    • 2022-04-11
    • 1970-01-01
    • 1970-01-01
    • 2023-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-15
    • 2019-02-11
    相关资源
    最近更新 更多