Skip to content

Commit

Permalink
fix ramda complement (#3634)
Browse files Browse the repository at this point in the history
Ramda's complement returns a function whose type is the same as the input
function provided. The runtime difference is that the boolean value is flipped
when the function is called. Flow doesn't handle expressing this very well, and
thus doesn't preserve the returned function's type. Until we have some mechanism
to do this, we can write a bunch of arity variants - or generate them, as was
done here. The libdef and its accompanying test are both generated.

If you want to generate higher arity to support or want to revisit the
generation, the original generation was done here:
https://github.com/LoganBarnett/typedef-gen
  • Loading branch information
LoganBarnett authored and AndrewSouthpaw committed Nov 14, 2019
1 parent 42924f7 commit b38de14
Show file tree
Hide file tree
Showing 7 changed files with 489 additions and 27 deletions.
53 changes: 50 additions & 3 deletions definitions/npm/ramda_v0.26.x/flow_v0.104.x-/ramda_v0.26.x.js
Original file line number Diff line number Diff line change
Expand Up @@ -2055,9 +2055,56 @@ declare module ramda {
y: (...args: Array<T>) => boolean
): (...args: Array<T>) => boolean;

declare function complement<T>(
x: (...args: Array<T>) => boolean
): (...args: Array<T>) => boolean;
// The following code is generated from
// https://github.com/LoganBarnett/typedef-gen due to Flow not being able to
// preserve the input function's form as a return type.
//
// Begin generated complement declaration.

declare function complement< Fn: () => boolean>(
f: Fn
): Fn;

declare function complement<A, Fn: (A) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, Fn: (A, B) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, Fn: (A, B, C) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, Fn: (A, B, C, D) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, E, Fn: (A, B, C, D, E) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, E, F, Fn: (A, B, C, D, E, F) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, E, F, G, Fn: (A, B, C, D, E, F, G) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, E, F, G, H, Fn: (A, B, C, D, E, F, G, H) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, E, F, G, H, I, Fn: (A, B, C, D, E, F, G, H, I) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, E, F, G, H, I, J, Fn: (A, B, C, D, E, F, G, H, I, J) => boolean>(
f: Fn
): Fn;
// End generated complement declaration.

declare function cond<A, B>(
fns: Array<[(...args: Array<A>) => boolean, (...args: Array<A>) => B]>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/*eslint-disable no-undef, no-unused-vars, no-console*/
import { describe, it } from 'flow-typed-test';
import _, {
complement,
compose,
curry,
filter,
Expand Down Expand Up @@ -47,13 +48,6 @@ const f = _.both(gt10, even);
const b: boolean = f('');
const b_: boolean = f(100);

//$ExpectError
const isEven = n => n % 2 === 0;
const isOdd = _.complement(isEven);

const c: boolean = isOdd('');
const c_: boolean = isOdd(2);

const fn = _.cond([
[_.equals(0), _.always('water freezes at 0°C')],
[_.equals(100), _.always(1)],
Expand All @@ -71,6 +65,126 @@ const def1: number = defaultTo42(undefined);
const feither = _.either(gt10, even);
const feitherR: boolean = f(101);

describe('complement', () => {
it('accepts a function that returns a boolean', () => {
complement(() => true)
complement(() => false)
// $ExpectError
complement(() => 'foo')
// $ExpectError
complement(() => 5)
// $ExpectError
complement(() => [])
// $ExpectError
complement(() => ({}))
// $ExpectError
complement(() => null)
// $ExpectError
complement(() => undefined)
})

it('returns a function that returns a boolean', () => {
const fn = complement(() => true)
const b: boolean = fn()
// $ExpectError
const s: string = fn()
// $ExpectError
const n: number = fn()
// $ExpectError
const xs: Array<mixed> = fn()
// $ExpectError
const obj: {[string]: mixed} = fn()
// $ExpectError
const v: void = fn()
// $ExpectError
const nil: null = fn()
})

// The following code is generated from
// https://github.com/LoganBarnett/typedef-gen due to Flow not being able to
// preserve the input function's form as a return type.
//
// Begin generated complement test cases.
it('returns a function whose parameters match the input function (0)', () => {
const fn = complement(() => true)
fn()
// Extra arguments are discarded, so there is no negative case here.
})

it('returns a function whose parameters match the input function (1)', () => {
const fn = complement((a: 'a',) => true)
fn('a',)
// $ExpectError
fn(0)
})

it('returns a function whose parameters match the input function (2)', () => {
const fn = complement((a: 'a', b: 'b',) => true)
fn('a', 'b',)
// $ExpectError
fn(0, 1)
})

it('returns a function whose parameters match the input function (3)', () => {
const fn = complement((a: 'a', b: 'b', c: 'c',) => true)
fn('a', 'b', 'c',)
// $ExpectError
fn(0, 1, 2)
})

it('returns a function whose parameters match the input function (4)', () => {
const fn = complement((a: 'a', b: 'b', c: 'c', d: 'd',) => true)
fn('a', 'b', 'c', 'd',)
// $ExpectError
fn(0, 1, 2, 3)
})

it('returns a function whose parameters match the input function (5)', () => {
const fn = complement((a: 'a', b: 'b', c: 'c', d: 'd', e: 'e',) => true)
fn('a', 'b', 'c', 'd', 'e',)
// $ExpectError
fn(0, 1, 2, 3, 4)
})

it('returns a function whose parameters match the input function (6)', () => {
const fn = complement((a: 'a', b: 'b', c: 'c', d: 'd', e: 'e', f: 'f',) => true)
fn('a', 'b', 'c', 'd', 'e', 'f',)
// $ExpectError
fn(0, 1, 2, 3, 4, 5)
})

it('returns a function whose parameters match the input function (7)', () => {
const fn = complement((a: 'a', b: 'b', c: 'c', d: 'd', e: 'e', f: 'f', g: 'g',) => true)
fn('a', 'b', 'c', 'd', 'e', 'f', 'g',)
// $ExpectError
fn(0, 1, 2, 3, 4, 5, 6)
})

it('returns a function whose parameters match the input function (8)', () => {
const fn = complement((a: 'a', b: 'b', c: 'c', d: 'd', e: 'e', f: 'f', g: 'g', h: 'h',) => true)
fn('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',)
// $ExpectError
fn(0, 1, 2, 3, 4, 5, 6, 7)
})

it('returns a function whose parameters match the input function (9)', () => {
const fn = complement((a: 'a', b: 'b', c: 'c', d: 'd', e: 'e', f: 'f', g: 'g', h: 'h', i: 'i',) => true)
fn('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',)
// $ExpectError
fn(0, 1, 2, 3, 4, 5, 6, 7, 8)
})

it('returns a function whose parameters match the input function (10)', () => {
const fn = complement((a: 'a', b: 'b', c: 'c', d: 'd', e: 'e', f: 'f', g: 'g', h: 'h', i: 'i', j: 'j',) => true)
fn('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',)
// $ExpectError
fn(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
})

// End generated complement test cases.
})


describe('ifElse', () => {
it('uses a union of both branches as the return type', () => {
const incCount = ifElse(x => x % 2 == 0, x => x + 1, x => x.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2016,9 +2016,56 @@ declare module ramda {
y: (...args: Array<T>) => boolean
): (...args: Array<T>) => boolean;

declare function complement<T>(
x: (...args: Array<T>) => boolean
): (...args: Array<T>) => boolean;
// The following code is generated from
// https://github.com/LoganBarnett/typedef-gen due to Flow not being able to
// preserve the input function's form as a return type.
//
// Begin generated complement declaration.

declare function complement< Fn: () => boolean>(
f: Fn
): Fn;

declare function complement<A, Fn: (A) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, Fn: (A, B) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, Fn: (A, B, C) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, Fn: (A, B, C, D) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, E, Fn: (A, B, C, D, E) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, E, F, Fn: (A, B, C, D, E, F) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, E, F, G, Fn: (A, B, C, D, E, F, G) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, E, F, G, H, Fn: (A, B, C, D, E, F, G, H) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, E, F, G, H, I, Fn: (A, B, C, D, E, F, G, H, I) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, E, F, G, H, I, J, Fn: (A, B, C, D, E, F, G, H, I, J) => boolean>(
f: Fn
): Fn;
// End generated complement declaration.

declare function cond<A, B>(
fns: Array<[(...args: Array<A>) => boolean, (...args: Array<A>) => B]>
Expand Down
53 changes: 50 additions & 3 deletions definitions/npm/ramda_v0.x.x/flow_v0.104.x-/ramda_v0.x.x.js
Original file line number Diff line number Diff line change
Expand Up @@ -2109,9 +2109,56 @@ declare module ramda {
y: (...args: Array<T>) => boolean
): (...args: Array<T>) => boolean;

declare function complement<T>(
x: (...args: Array<T>) => boolean
): (...args: Array<T>) => boolean;
// The following code is generated from
// https://github.com/LoganBarnett/typedef-gen due to Flow not being able to
// preserve the input function's form as a return type.
//
// Begin generated complement declaration.

declare function complement< Fn: () => boolean>(
f: Fn
): Fn;

declare function complement<A, Fn: (A) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, Fn: (A, B) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, Fn: (A, B, C) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, Fn: (A, B, C, D) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, E, Fn: (A, B, C, D, E) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, E, F, Fn: (A, B, C, D, E, F) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, E, F, G, Fn: (A, B, C, D, E, F, G) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, E, F, G, H, Fn: (A, B, C, D, E, F, G, H) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, E, F, G, H, I, Fn: (A, B, C, D, E, F, G, H, I) => boolean>(
f: Fn
): Fn;

declare function complement<A, B, C, D, E, F, G, H, I, J, Fn: (A, B, C, D, E, F, G, H, I, J) => boolean>(
f: Fn
): Fn;
// End generated complement declaration.

declare function cond<A, B>(
fns: Array<[(...args: Array<A>) => boolean, (...args: Array<A>) => B]>
Expand Down

0 comments on commit b38de14

Please sign in to comment.