Как указать список генериков неизвестного / произвольного размера - PullRequest
0 голосов
/ 14 февраля 2019

Примечание : я начал обсуждение на Github на эту тему.

У меня есть функция zip , сейчас она набранадля итераций одного типа T.Я хотел бы иметь этот тип для произвольного смешанного типа ввода, но при этом сохраняя соответствующий тип вывода, например, если тип ввода [Iterable<T>, Iterable<U>] Я хочу, чтобы тип вывода был Iterable<[T, U]>.Возможно ли иметь это для произвольного размера ввода?Я в основном хочу сказать, если у вас есть этот список типов в качестве ввода, вы будете иметь их в качестве вывода .

Вот текущая версия моего почтового индекса :

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.

Может быть, это нужно определить рекурсивно в некоторых языках, что также было бы интересно увидеть.

Ответы [ 2 ]

0 голосов
/ 25 февраля 2019

Вот рабочее решение.Весь кредит идет на jbrown215 от команды Flow, он нашел идею , используя $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;
}
0 голосов
/ 15 февраля 2019

Мы смогли достичь этого только путем переопределения объявления сигнатуры функции.Это может помочь:

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