【发布时间】: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<Iterable<x>> -> Iterable<Array<x>> 总是调用已知类型x。因此,我们应该能够静态决定在给定x 的情况下f 将返回什么类型(x 是单个泛型类型还是泛型类型列表)。
函数本身也是如此,如果你有一个类型 x 作为输入,那么你只需要检查你的函数是否保留类型 x。
也许这需要在某些语言中递归定义,这也很有趣。
【问题讨论】:
标签: flowtype typechecking static-typing