我有一个帮助函数库,我想导出curried版本。
它的一小部分看起来像这样:
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)
但这并不奏效。 Flow抱怨表达式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);
}
但此时我基本上会为我要导出的每个功能重写一大块咖喱。
有没有更好的方法来帮助Flow填充const表达式中导出的泛型类型变量?
答案 0 :(得分:2)
请在此处查看我对同一问题的回答:https://github.com/facebook/flow/issues/2165#issuecomment-236868389
这里的主要限制是Flow根本不会推断出多态类型。特别是,每当它看到对多态函数的调用时,它立即用新类型参数实例化类型参数,结果永远不会像Hindley-Milner系统(https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system)那样“泛化”。
这种限制的原因是这种多态类型推断对于子类型是不可判定的(参见Pierce,“有界量化是不可判定的”,POPL'92),并且子类型是JavaScript的必要特征(但不是很多)类似ML的语言。)
答案 1 :(得分:0)
我怀疑在没有更高级别的类型
的情况下无法定义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?
答案 2 :(得分:0)
具有子类型,多态性和紧凑类型的类型推断在传统上一直存在问题。 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的价值限制。