Можно ли набирать рекурсивные функции c в Typescript? - PullRequest
1 голос
/ 20 января 2020

У меня есть следующая универсальная c функция, из которой я могу получить различные полезные функции c:

const variadic = f => {
  const go = args =>
    Object.defineProperty(
      arg => go(args.concat([arg])),
      "runVariadic",
      {get: function() {return f(args)}, enumerable: true});

  return go([]);
};

const arrFold = alg => zero => xs =>
  xs.reduce((acc, x) => alg(acc) (x), zero);

const comp = f => g => x => f(g(x));
const id = x => x;

const varComp = variadic(arrFold(comp) (id));

const inc = x => x + 1;

const main = varComp(inc) (inc) (inc) (inc) (inc);

console.log(
  main.runVariadic(0)); // 5

Этот тип рекурсивно-вариационного интерфейса c позволяет мне поддерживать плоский синтаксис приложения, не полагаясь на цепочку методов. Дополнительно я могу частично применять и составлять такие функции. К сожалению, variadic и производные varComp имеют бесконечные типы. Я смутно припоминаю, что в Haskell в любом случае есть способ набирать такие функции, но это требует большого количества машин типов, расширенных языковых возможностей. Есть ли хитрость для их ввода в Typescript?

Я новичок в Typescript ie, поэтому даже не знаю, с чего начать.

1 Ответ

3 голосов
/ 21 января 2020

Большое предостережение заключается в том, что почти нет шансов, что компилятор TypeScript сможет выводить типы так, как вы этого хотите; вам, вероятно, нередко придется вручную задавать параметры типа или даже утверждать , что конкретная функция является обобщенной c. TypeScript не Haskell, и он не пытается (много).

При этом, вот один из возможных вариантов ввода для variadic:

interface Variadic<T, U> {
  (x: T): Variadic<T, U>
  runVariadic: U,
}

const variadic = <T, U>(f: (args: T[]) => U) => {
  const go = (args: T[]): Variadic<T, U> =>
    Object.defineProperty(
      (arg: T) => go(args.concat([arg])),
      "runVariadic",
      { get: function () { return f(args) }, enumerable: true });

  return go([]);
}

Идея в том, что variadic принимает функцию, принимающую массив T и возвращающую U, и превращает ее в Variadic<T, U>. Variadic<T, U> - это функция, которая принимает аргумент T и возвращает Variadic<T, U>, а также имеет свойство runVariadic типа U.


Вот краткий тест:

const str = variadic((args: string[]) => args)("hey")("you")("guys").runVariadic; // string[]
console.log(str) // ["hey", "you", "guys"]

Здесь я передаю variadic функцию id, которая аннотирована для получения и возврата массива строк. Затем полученный Variadic<string, string[]> может принимать любое количество string аргументов один за другим, и, наконец, его свойство runVariadic выводится компилятором как string[], что подтверждается журналом консоли.


Для вашего тестового кода необходимо много ручного ввода и подтверждения:

const arrFold = <T, U>(alg: (x: T) => (y: U) => T) => (zero: T) => (xs: U[]) =>
  xs.reduce((acc, x) => alg(acc)(x), zero);
const comp = <T, U>(f: (x: T) => U) => <V>(g: (x: V) => T) => (x: V) => f(g(x));
const id = <T>(x: T) => x;

const varComp = variadic(arrFold(comp)(id)) as
  Variadic<(x: number) => number, (x: number) => number>;

const inc = (x: number) => x + 1;

const main = varComp(inc)(inc)(inc)(inc)(inc);
console.log(
  main.runVariadic(0)); // 5

Ввод arrFold, comp и id достаточно прост, но результирующий тип varComp, выведенный компилятором, пронизан unknown s. Вместо этого я утверждал, что это Variadic<(x: number) => number, (x: number) => number>, поскольку я знаю, что мы передадим ему inc. Так что main.runVariadic выводится как (x: number) => number), что тоже выглядит неплохо.


Хорошо, надеюсь, это даст вам некоторое направление. Удачи!

Детская площадка ссылка на код

...