# Writing Flowtype annotations for Ramda: Polymorphism and *R.any*

### Writing more generic types with polymorphism

We’re finally going to put *add* and *subtract* to rest, but transition into a discussion on polymorphism by defining *NumericFn2* type in terms of the more general type *CurriedFn2*:

declare type CurriedFn2<T1, T2, R> =

& ((t1: T1, t2: T2) => R)

& ((t1: T1, ...rest: Array<void>) => (t2: T2) => R)

declare type NumericFn2 = CurriedFn2<number, number, number>

this is how the library definition in flow-typed has it defined.

This technique uses polymorphism to define a type which, when provided with other types *T1*, *T2*, and *R* produces a concrete type. For example, if we plug in number in every slot of *CurriedFn2*, we get the exact definition we had before:

declare type CurriedFn2WithNumber<number, number, number> =

& ((t1: number, t2: number) => number)

& ((t1: number, ...rest: Array<void>) => (t2: number) => number)

#### Defining a type for R.any

The Ramda documentation for *any* describes it as a function that “Returns true if both arguments are true; false otherwise.” However, it gives the type signature as ** → * → **, instead of the more restrictive *boolean → boolean → boolean*.

Indeed, you can put any values that have an interpretation as truth or falsy in javascript, and the result of *and(x, y)* is either the last truthy value, or the first falsy value:

and(true, false); // => false

and(0, 'a'); // => 0

and('', false); // => ''

and([], 0); // => 0

and(() => {}, 'function'); // => 'function'

Arguably, this is not a very good use for *and*, but it is valid and hinted at by that type signature!

*and* is polymorphic, but in a “bounded” sense. Most likely, the implementation of *and* is based on the double-equals (*==*) operator in JavaScript and will only work with values compatible with the operator. In Haskell, we might define something like *and* in terms of the *Eq* typeclass.

So it turns out that doing this sort of works, but might not be expresssive enough:

declare var and: CurriedFn2<any, any, any>

because it wouldn’t catch something like

and(1, 'a') == [];

Which would not cause a runtime error, but is the kind of thing Flow tries to catch by default.

If the first argument of *and* is *T* and second is *U*, then the result is either *T* or *U*, and can’t be something random. Thus, a better type for the 2-ary version of *and* would be *(x: T, y: U) => T | U*.

declare function and<T1, T2>(x: T1, y: T2, ...rest: Array<void>): T1 | T2;

Then we get the error we expect:

and(1, 'a') == [];

// 12: a == [];

// ^ number. Cannot be compared to

// 12: a == [];

// ^ empty array literal

// 12: a == [];

// ^ string. Cannot be compared to

// 12: a == [];

// ^ empty array literal

To get the unary version, add a separate declaration:

declare function and<T1, T2>(x: T1, ...rest: Array<void>): (y: T2) => T1 | T2;

declare function and<T1, T2>(x: T1, y: T2, ...rest: Array<void>): T1 | T2;

It seems that *the order matters*. If you put the 2-ary definition before the unary, Flow will throw an error on something like *and(1)(1)* complaining about not having enough arguments.

So we know that the return type of *and(1, ‘a’)* is *string | number*, which means this code will typecheck correctly:

and(1, 'a') == []; // error

and(1, 'a') + '!' // no error

and(1)('a') + '!' // no error

any("ada")([0,0]).concat("A") // no error

Although, weirdly, this does not work!

any(["a", "a"])([0,0]).concat("A")

// 19: any(["a", "a"])([0,0]).concat("A")

// ^ string. This type is incompatible with

// 19: any(["a", "a"])([0,0]).concat("A")

// ^ number

// 19: any(["a", "a"])([0,0]).concat("A")

// ^ number. This type is incompatible with

// 19: any(["a", "a"])([0,0]).concat("A")

// ^ string

This might be an issue specific to unions of tuples. There are some issues on this on Flow Github repository.

### What’s Next?

In the next post, we’ll start looking at functions for lists (e.g. map, filter) in Ramda.