【问题标题】:Can recursively variadic functions be typed in Typescript?可以在 Typescript 中输入递归可变参数函数吗?
【发布时间】:2020-05-06 09:05:14
【问题描述】:

我有以下通用函数,我可以从中派生出各种有用的可变参数函数:

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

这种递归可变参数接口使我能够在不依赖方法链的情况下保持扁平的应用程序语法。此外,我可以部分应用和组合这些功能。不幸的是variadic 和派生的varComp 有无限类型。我隐约记得在 Haskell 中有一种方法可以键入此类函数,但它需要大量类型机器,namley 高级语言扩展。在 Typescript 中输入它们有什么技巧吗?

我是 Typescript 新手,所以我什至不知道从哪里开始。

【问题讨论】:

  • 不,你不能在 TypeScript 中输入这个。为此,您需要依赖类型或类型类,TypeScript 都不支持。顺便说一句,你为什么还要写这样的代码?方法链有什么问题?
  • @AaditMShah 因为它最类似于 Haskell 和 Koltin 的中缀表示法。如果您知道一种替代方法,可以避免函数嵌套,而不会失去部分应用、组合和惰性的能力,并且具有适当的类型,请回复,我将排除它。

标签: javascript typescript types functional-programming variadic


【解决方案1】:

这里最大的警告是,TypeScript 的编译器几乎不可能按照您希望的方式推断类型;您可能会经常发现自己需要手动指定类型参数,甚至需要 assert 指定特定函数是通用函数。 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&lt;T, U&gt;Variadic&lt;T, U&gt; 是一个接受T 参数并返回Variadic&lt;T, U&gt; 的函数,它还具有U 类型的runVariadic 属性。


这是一个简短的测试:

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

在这里,我将 variadic 传递给 id 函数,该函数被注释为获取并返回字符串数组。然后生成的Variadic&lt;string, string[]&gt; 可以一个接一个地接受任意数量的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

arrFoldcompid 的类型相当简单,但编译器推断的 varComp 的结果类型充满了unknowns。相反,我断言它是Variadic&lt;(x: number) =&gt; number, (x: number) =&gt; number&gt;,因为我知道我们会将inc 传递给它。所以main.runVariadic被推断为(x: number) =&gt; number),看起来也不错。


好的,希望能给你一些指导。祝你好运!

Playground link to code

【讨论】:

    猜你喜欢
    • 2022-01-25
    • 2012-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-16
    • 2011-03-05
    • 1970-01-01
    相关资源
    最近更新 更多