Ramda的Flow typings

时间:2017-08-27 18:42:13

标签: generics functional-programming flowtype currying ramda.js

Ramda的流程类型包含以下curried function definitions

  declare type __CurriedFunction1<A, R, AA: A> =
    & ((...r: [AA]) => R)
  declare type CurriedFunction1<A, R> = __CurriedFunction1<A, R, *>

  declare type __CurriedFunction2<A, B, R, AA: A, BB: B> =
    & ((...r: [AA]) => CurriedFunction1<BB, R>)
    & ((...r: [AA, BB]) => R)
  declare type CurriedFunction2<A, B, R> = __CurriedFunction2<A, B, R, *, *>

  // ...

我不理解____CurriedFunctionN助手和AABB类型的需要,即为什么CurriedFunction1<A, R>不能被定义为:

  declare type CurriedFunction1<A, R> =
    & ((...r: [A]) => R)

1 个答案:

答案 0 :(得分:2)

其原因与类型差异有关。如果我说起初我也不理解这一点,那可能更有意义,并解释我是如何尝试理解它的。

在类型的定义中,唯一的区别是AA: A表示&#34; AA是A&#34;的完全或子类型。所以我认为原因与子类如何被视为函数的参数有关。

所以我使用更简单的类型版本重新创建了一个简单的例子,并将子类型作为参数传递:

declare type MyCurriedFunction1<A, R> =
  & ((...r: [A]) => R)

let plus2: MyCurriedFunction1<number, number> = (x) => x+2;

let y: 1 | 2 = 1;
plus2(x);

果然,这会引发错误:

18: let plus2: MyCurriedFunction1<number, number> = (x) => x+2;
                                  ^ number. This type is incompatible with
21: plus2(x);
          ^ number enum (1 | 2)

Flow告诉我们,使用这个更简单的函数类型,它不能接受参数上比(1 | 2)定义的更具体的类型(number)。这与称为类型方差的想法有关,该概念是关于何时可以安全地使用子(或超级)类型代替彼此。维基百科有一个sort-of-readable explanation为什么数组/元组类型通常是不变的:元组可以读取(允许协方差)或写入(这将允许逆变),所以为了两种可能性的安全,你只能使用完全相同的类型。

在这种情况下,似乎很清楚我们希望允许我们的函数采用其参数类型的子类型。默认情况下,这不会发生[A],这是参数的一个参数类型的元组,因此我们必须明确允许为参数使用子类型。

因此,通过将类型声明为Ramda,我们现在可以将子类型作为参数传递给使用此类型的函数。

declare type __CurriedFunction1<A, R, AA: A> =
    & ((...r: [AA]) => R)
declare type CurriedFunction1<A, R> = __CurriedFunction1<A, R, *>

let plus1: CurriedFunction1<number, number> = (x) => x+1;

let x: 1 | 2 = 1;
plus1(x);

我们没有任何类型错误!