如何指定未知/任意大小的泛型列表

时间:2019-02-14 11:17:03

标签: flowtype typechecking static-typing

注意:我开始对此主题进行discussion on Github的访问。

我有一个 zip 函数,现在输入的是相同类型T的可迭代对象。我想为任意混合输入类型输入此类型,但仍保留匹配的输出类型,例如,如果输入类型[Iterable<T>, Iterable<U>]我希望输出类型为Iterable<[T, U]>。可以为任意输入大小使用此功能吗?我基本上要说的是,如果您将此类型列表作为输入,则将其作为输出

这是我的 zip 的当前版本:

export function *zip<T>(...iterables:Array<Iterable<T>>): Iterable<Array<T>> {
   const iterators = iterables.map(iterable => iter(iterable));
   while(true){
      const items = iterators.map(iterator => iterator.next());
      if (items.some(item => item.done)){
         return;
      }
      yield ((items.map(item => { return item.value }): Array<any>): Array<T>);
  }
}

export function *iter<T>(iterable:Iterable<T>): Iterator<T> {
   yield* iterable;
}

当前best solutionAndrewSouthpaw开发:

declare function zip<A, B>(Iterable<A>, Iterable<B>): Iterable<[A, B]>;
declare function zip<A, B, C>(Iterable<A>, Iterable<B>, Iterable<C>): Iterable<[A, B, C]>;
declare function zip<A, B, C, D>(Iterable<A>, Iterable<B>, Iterable<C>, Iterable<D>): Iterable<[A, B, C, D]>;
export function *zip<T>(...iterables:Array<Iterable<T>>): Iterable<Array<T>> {
   const iterators = iterables.map(iterable => iter(iterable));
   while(true){
      const items = iterators.map(iterator => iterator.next());
      if (items.some(item => item.done)){
         return;
      }
      yield ((items.map(item => { return item.value }): Array<any>): Array<T>);
  }
}

使用4、3或2个可迭代对象进行调用时,它按预期方式工作,使用5个或更多参数进行调用时,流程仅表示zip只能使用4个或更少的参数进行调用。当然,我们可以添加任意数量的函数签名,以使其适用于5、6或任意数量的N个参数,但这将需要声明N个不同的签名(这有点丑陋)。另一方面,这种策略不允许有无数个参数(就像散布运算符一样)。我还在寻找。


这提出了一个更普遍的问题,是否存在任何语言?

我真的感觉这可以在理论上完成(不一定要顺其自然),另一方面,我不记得我已经/看到过的静态类型语言(我也会有兴趣看到以任何语言进行的这种类型检查)。

更具体地说,我的感觉是,如果您有一个类型检查系统,其中(根据定义)所有类型都是静态已知的(任何变量的类型均为x),则函数{{1 }}总是在已知类型f: Array<Iterable<x>> -> Iterable<Array<x>>上调用。因此,我们应该能够静态地确定给定x将返回哪种类型fx是单个泛型类型还是泛型类型列表)。

对于函数本身也是如此,如果您将类型x作为输入,那么您只需要检查函数是否保留类型x

也许这需要在某些语言中递归定义,这也很有趣。

2 个答案:

答案 0 :(得分:2)

我们只能通过覆盖函数签名声明来完成此操作。这可能会有所帮助:

declare function zip<A, B>(Iterable<A>, Iterable<B>): Iterable<[A, B]>
declare function zip<A, B, C>(Iterable<A>, Iterable<B>, Iterable<C>): Iterable<[A, B, C]>
declare function zip<A, B, C, D>(Iterable<A>, Iterable<B>, Iterable<C>, Iterable<D>): Iterable<[A, B, C, D]>
export function zip(a, b, c, d) {
  /* ... */
}

答案 1 :(得分:0)

这是有效的解决方案。一切归功于Flow团队的jbrown215,他在这里找到了using $ReadOnlyArray<mixed>的想法:

export function *zip<T: $ReadOnlyArray<mixed>>(...iterables:Array<Iterable<T>>): Iterable<Array<T>> {
   const iterators = iterables.map(iterable => iter(iterable));
   while(true){
      const items = iterators.map(iterator => iterator.next());
      if (items.some(item => item.done)){
         return;
      }
      yield ((items.map(item => { return item.value }): Array<any>): Array<T>);
  }
}

export function *iter<T>(iterable:Iterable<T>): Iterator<T> {
   yield* iterable;
}