在Ramda的流量分型咖喱函数定义
问题描述:
Ramda的流动分型有以下curried function definitions:在Ramda的流量分型咖喱函数定义
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
帮手和AA
,BB
类型的需要,即为什么不能CurriedFunction1<A, R>
被定义为:
declare type CurriedFunction1<A, R> =
& ((...r: [A]) => R)
答
其原因与类型差异有关。如果我说起初我不明白这一点,那么这可能会更有意义,并解释我是如何通过试验来理解它的。
在这些类型的定义中,唯一的区别是AA: A
意味着“AA完全或A的子类型”。所以我认为这个原因与如何将亚型视为函数的参数有关。
所以我使用你的类型的简化版本,并在子类型作为参数传递重新创建一个简单的例子:
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)
流量是告诉我们用这个更简单的函数类型的版本,它不能接受比(number
)定义的参数更具体的类型(1 | 2
)。这与一个叫做类型差异的想法有关,它是关于什么时候我们可以安全地使用子类(或超类)来代替对方。*有一个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);
而且我们没有得到任何类型的错误!
无论你的问题如何,对于像curried函数这样的简单构造来说,这似乎相当复杂。我的意思是variadic函数(带有一个rest参数)很难。参数多态论据很难。但从类型层面来看,一系列一元函数似乎相当容易。 – ftor