导出 curried 泛型函数时遇到问题

Trouble exporting curried generic function

我有一个辅助函数库,我想导出它的柯里化版本。

它的一小部分看起来像这样:

export function curry2<A,B,C>(f: (x: A, y: B) => C): (x: A) => (y: B) => C {
  return (a) => (b) => f(a, b)
}

function _push<A>(item: A, items: Array<A>): Array<A> {
  return items.concat(item)
}

export const push = curry2(push)

但这不起作用。流抱怨表达curry2(push),说:

- type parameter 'A' of function call. Missing annotation.
- type parameter 'B' of function call. Missing annotation.

所以我试图通过注释导出的标识符来解决这个问题:

export const push<A>: (item: A) => (items: Array<A>) => Array<A>

但这行不通,因为const表达式不能引入泛型类型变量。

所以我想我必须导出一个实际函数才能对其进行注释:

export function push<A> (item: A): (items: Array<A>) => Array<A> {
  return curry2(_push)(item);
}

但在这一点上,我基本上要为每个要导出的函数重写一大块 curry。

有没有更好的方法帮助Flow在const表达式中填充exports的泛型类型变量?

我怀疑没有更高种类的类型

无法定义curry2函数
type Function2<A, B, C> = (a: A, b: B) => C;
type CurriedFunction2<A, B, C> = (a: A) => (b: B) => C;

export function curry2<A, B, C>(f: Function2<A, B, C>): CurriedFunction2<A, B, C> {
  return (a: A) => (b: B): C => f(a, b)
}

export const push = curry2(function <A>(item: A, items: Array<A>): Array<A> {
  return items.concat(item)
})

最后一个定义意味着 push 具有类型

push: CurriedFunction2<A, Array<A>, Array<A>> // <= higher kinded type?

在这里查看我对同一问题的回答:https://github.com/facebook/flow/issues/2165#issuecomment-236868389

这里的主要限制是 Flow 根本不推断多态类型。特别是,每当它看到对多态函数的调用时,它会立即使用新的类型参数实例化类型参数,并且结果永远不会 "generalized" 回到 Hindley-Milner 系统 (https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system) 的方式。

这个限制的原因是这种多态类型推断是不可判定的子类型(参见 Pierce,"Bounded quantification is undecidable",POPL'92),子类型是 JavaScript 的必要特征(但对于类似 ML 的语言来说就不是那么多了。

子类型化、多态性和紧凑类型的类型推断传统上一直存在问题。有 a recent paper describing a modification to Hindley-Milner inference 如果它可以适应 Flow 可能会有用。

无论如何,Flow 实际上似乎允许一些多态 const 值。 For example

// @flow strict

const id: <T>(T) => T = <T>(a: T):T => a;
const genericConstant: <T>(T) => T = id(id);

const dup: <U>(U) => [U, U] = <U>(a: U) => [a, a];
const iddup: <T>(T) => [T, T] = id(dup);
const dupdup: [<T>(T) => [T, T], <U>(U) => [U, U]] = dup(dup);

const curry2 = <A,B,C>(f: (A, B) => C) => (a: A) => (b: B): C => f(a, b);

function _push<A>(item: A, items: Array<A>): Array<A> {
  return items.concat(item)
}

const push2 /*broken : <A>(A) => Array<A> => Array<A> */ = curry2(_push)

const pair: <A,B>(A, B) => [A, B] = <A, B>(a: A, b: B): [A, B] => [a, b]

const pair2 /*broken: <A,B>(A) => (B) => [A, B]*/ = curry2(pair)

不知道是遵循什么规则,但肯定和SML的取值限制不同