Reputation: 44947
Ramda's flow typings have the following 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, *, *>
// ...
I don't understand the need of ____CurriedFunctionN
helpers and AA
, BB
types, i.e. why can't CurriedFunction1<A, R>
be defined as:
declare type CurriedFunction1<A, R> =
& ((...r: [A]) => R)
Upvotes: 2
Views: 562
Reputation: 2287
The reason for this has to do with type variance. It probably makes more sense if I say that I didn't understand this at first, either, and explain how I experimented my way into understanding it.
In the definitions of the types, the only difference is that AA: A
means "AA is exactly or a subtype of A". So I assumed that the reason had something to do with how subtypes are treated as arguments of the functions.
So I recreated a simple example using your simpler version of the type and passing in a subtype as an argument:
declare type MyCurriedFunction1<A, R> =
& ((...r: [A]) => R)
let plus2: MyCurriedFunction1<number, number> = (x) => x+2;
let y: 1 | 2 = 1;
plus2(x);
Sure enough, this throws an error:
18: let plus2: MyCurriedFunction1<number, number> = (x) => x+2;
^ number. This type is incompatible with
21: plus2(x);
^ number enum (1 | 2)
Flow is telling us that with this simpler version of the function type, it cannot accept a more specific type (1 | 2
) on an argument than it was defined with (number
). This is related to an idea called type variance, which is about when we can safely use sub (or super) types in place of each other. Wikipedia has a sort-of-readable explanation of why array/tuple types are normally invariant: tuples can be read from (which would allow covariance) or written to (which would allow contravariance), so to be safe for both possibilities you can only use exactly the same type.
In this case, though, it seems pretty clear that we want our function to be allowed to take subtypes of its argument types. This won't happen by default for [A]
, which is a tuple of the argument's one argument type, so we have to explicitly allow using a subtype for the argument.
So by declaring the types as Ramda does, we are now allowed to pass in subtypes as arguments to functions using this type.
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);
And we don't get any type error!
Upvotes: 2