如何指定 unknown/arbitrary 大小的泛型列表
How to specify a list of generics of unknown/arbitrary size
注意:我开始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;
}
当前 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 个或更多参数调用时,flow 将简单地说 zip 只能使用 4 个或更少参数调用。当然,我们可以根据需要添加尽可能多的函数签名,使其适用于 5、6 或任意数量的 N 个参数,但这需要声明 N 个不同的签名(这有点难看)。另一方面,这种策略不允许有无限数量的参数(就像扩展运算符那样)。我还在找那个。
这提出了一个更普遍的问题,是否存在任何语言?
我真的觉得这可以在理论上完成(不一定在流程中),另一方面,我不记得有一种静态类型的语言 done/seen (我也有兴趣在任何语言中看到这种类型检查)。
更具体一点,我的感觉是,如果你有一个类型检查系统,其中(根据定义)所有类型都是静态已知的(任何变量都具有已知类型 x
),那么函数 f: Array<Iterable<x>> -> Iterable<Array<x>>
总是在已知类型 x
上调用。因此,我们应该能够静态地决定 f
将 return 给定 x
的类型(无论 x
是单个泛型类型还是泛型类型列表)。
函数本身也是如此,如果你有一个类型x
作为输入,那么你只需要检查你的函数是否保留类型x
。
也许这需要在某些语言中递归定义,这也很有趣。
我们只能通过覆盖函数签名声明来完成此操作。这可能有帮助:
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) {
/* ... */
}
这是可行的解决方案。所有功劳归于 jbrown215 from Flow team, he found the idea of 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;
}
注意:我开始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;
}
当前
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 个或更多参数调用时,flow 将简单地说 zip 只能使用 4 个或更少参数调用。当然,我们可以根据需要添加尽可能多的函数签名,使其适用于 5、6 或任意数量的 N 个参数,但这需要声明 N 个不同的签名(这有点难看)。另一方面,这种策略不允许有无限数量的参数(就像扩展运算符那样)。我还在找那个。
这提出了一个更普遍的问题,是否存在任何语言?
我真的觉得这可以在理论上完成(不一定在流程中),另一方面,我不记得有一种静态类型的语言 done/seen (我也有兴趣在任何语言中看到这种类型检查)。
更具体一点,我的感觉是,如果你有一个类型检查系统,其中(根据定义)所有类型都是静态已知的(任何变量都具有已知类型 x
),那么函数 f: Array<Iterable<x>> -> Iterable<Array<x>>
总是在已知类型 x
上调用。因此,我们应该能够静态地决定 f
将 return 给定 x
的类型(无论 x
是单个泛型类型还是泛型类型列表)。
函数本身也是如此,如果你有一个类型x
作为输入,那么你只需要检查你的函数是否保留类型x
。
也许这需要在某些语言中递归定义,这也很有趣。
我们只能通过覆盖函数签名声明来完成此操作。这可能有帮助:
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) {
/* ... */
}
这是可行的解决方案。所有功劳归于 jbrown215 from Flow team, he found the idea of 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;
}