Примечание. Я начал дискуссию на 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;
}
Текущее лучшее решение от 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
. Следовательно, мы должны быть в состоянии статически решить, какой тип f
будет возвращен при заданном x
(будь то x
одиночный универсальный тип или список универсальных типов).
То же самое касается и самой функции, если у вас на входе тип x
, то вам нужно только проверить, что ваша функция сохраняет тип x
.
Возможно, это нужно определить рекурсивно в некоторых языках, это тоже было бы интересно посмотреть.