【问题标题】:How to specify a list of generics of unknown/arbitrary size如何指定未知/任意大小的泛型列表
【发布时间】:2019-07-08 09:46:10
【问题描述】:

注意:我针对这个主题发起了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 solution by AndrewSouthpaw

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),那么函数 f: Array&lt;Iterable&lt;x&gt;&gt; -&gt; Iterable&lt;Array&lt;x&gt;&gt; 总是调用已知类型x。因此,我们应该能够静态决定在给定x 的情况下f 将返回什么类型(x 是单个泛型类型还是泛型类型列表)。

函数本身也是如此,如果你有一个类型 x 作为输入,那么你只需要检查你的函数是否保留类型 x

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

【问题讨论】:

    标签: flowtype typechecking static-typing


    【解决方案1】:

    我们只能通过重写函数签名声明来实现这一点。这可能会有所帮助:

    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) {
      /* ... */
    }
    

    【讨论】:

    • 谢谢。这几乎就是我要找的。您是否认为不仅可以对 3 个,而且对任何数量(可能很大)的可迭代对象都可以使用相同的值?理想情况下,我什至想要一些适用于无限制输入大小的东西,但我想这很难实现(而且可能有点矫枉过正)。
    • 我还没有找到方法。您总是可以尝试使用流程打开一个问题,但这是一个好坏参半的问题。在像你这样的情况下,我只是创建了一些愚蠢的重载来容纳大量的迭代。如果您说的是 10+ 或 100 或 1000 多个,那么我只能接受我们正在尝试将类型放在动态语言上,而就其本质而言,我们无法拥有一切。
    • 我不确定为什么它不可能(不是通过流量,而是在理论上)。我想我会尝试就此提出一个问题,只是为了理解为什么在这种情况下不可能。我会随时更新我的​​问题。
    【解决方案2】:

    这是有效的解决方案。所有功劳归功于 Flow 团队的jbrown215,他在这里找到了using $ReadOnlyArray&lt;mixed&gt; 的想法:

    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;
    }
    

    【讨论】:

      猜你喜欢
      • 2020-04-07
      • 1970-01-01
      • 1970-01-01
      • 2022-12-12
      • 2016-06-08
      • 1970-01-01
      • 2021-10-19
      • 2011-07-09
      • 2015-06-25
      相关资源
      最近更新 更多