首页 > 解决方案 > 为什么 Dart 不能推断 List.fold() 的类型?

问题描述

我正在尝试List.fold()使用 Dart 2.1.0:

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

它返回一个错误:

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

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

似乎它无法推断出xto be的类型int,所以不知道如何在+那里应用。

根据方法规范x应该与初始值具有相同的类型0,这显然是一个int. 为什么 Dart 不能推断出来?

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

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

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

标签: dartfluttertype-inference

解决方案


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填充foldT的泛型类型参数:

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 的限制吗?

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

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

考虑通用方法:

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

及其用法:

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

有两种方法可以推断 的类型T

  1. fold返回值被赋值给一个Object obj0是一个,Object因为它是一个int,然后T“解析到”Object

  2. fold第一个参数是 an int,返回值应该是 anObject并且int是 an Object,然后T“解析为”int

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

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

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

例如,此代码段对路径 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);

}

推荐阅读