Skip to content

Commit

Permalink
Improve Exhaustive matching performances
Browse files Browse the repository at this point in the history
  • Loading branch information
gvergnaud committed Feb 8, 2021
1 parent 9a11934 commit 1f44061
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 10 deletions.
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ const builder = <a, b>(
* that **all cases are handled**. `when` predicates
* aren't supported on exhaustive matches.
**/
exhaustive: (): ExhaustiveMatch<DistributeUnions<a>, b> =>
exhaustive: (): ExhaustiveMatch<DistributeUnions<a>, a, b> =>
builder(value, patterns) as any,
});

Expand Down
15 changes: 11 additions & 4 deletions src/types/Match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export type EmptyMatch<i, o> = Match<i, o> & {
* that **all cases are handled**. `when` predicates
* aren't supported on exhaustive matches.
**/
exhaustive: () => ExhaustiveMatch<DistributeUnions<i>, o>;
exhaustive: () => ExhaustiveMatch<DistributeUnions<i>, i, o>;
};

type NonExhaustivePattern<i> = { __nonExhaustive: never } & i;
Expand All @@ -125,7 +125,7 @@ type NonExhaustivePattern<i> = { __nonExhaustive: never } & i;
* ### ExhaustiveMatch
* An interface to create an exhaustive pattern matching clause.
*/
export type ExhaustiveMatch<i, o> = {
export type ExhaustiveMatch<distributedInput, i, o> = {
/**
* ### Match.with
* If the data matches the pattern provided as first argument,
Expand All @@ -138,7 +138,12 @@ export type ExhaustiveMatch<i, o> = {
selections: ExtractSelections<i, p>
) => PickReturnValue<o, c>
): ExhaustiveMatch<
Exclude<i, ExtractPreciseValue<i, InvertPattern<p>>>,
// For performances, we pass both the original input and
// the distributedInput to ExhaustiveMatch, so we can compute the pattern
// from the original input, which is much faster than computing it
// from the distributed one.
Exclude<distributedInput, ExtractPreciseValue<i, InvertPattern<p>>>,
i,
PickReturnValue<o, c>
>;

Expand All @@ -159,5 +164,7 @@ export type ExhaustiveMatch<i, o> = {
* every cases, and you should probably add a another `.with(...)` clause
* to prevent potential runtime errors.
* */
run: [i] extends [never] ? () => o : NonExhaustivePattern<i>;
run: [distributedInput] extends [never]
? () => o
: NonExhaustivePattern<distributedInput>;
};
29 changes: 24 additions & 5 deletions tests/exhaustive-match.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,7 @@ describe('exhaustive()', () => {
.exhaustive()
.with('a', (x) => 1)
.with('b', (x) => 1)
// twice the same match.exhaustive()
// @ts-expect-error
.with('b', (x) => 1)
.run();

match(input)
Expand Down Expand Up @@ -97,9 +95,7 @@ describe('exhaustive()', () => {
.exhaustive()
.with(1, (x) => 1)
.with(2, () => 3)
// twice the same match.exhaustive()
// @ts-expect-error
.with(2, () => 3)
.run();

match(input)
Expand Down Expand Up @@ -515,12 +511,35 @@ describe('exhaustive()', () => {
});

it('should work with generics', () => {
const f = <a>(xs: a[]) =>
const last = <a>(xs: a[]) =>
match<a[], Option<a>>(xs)
.exhaustive()
.with([], () => none)
.with(__, (xs) => some(xs[xs.length - 1]))
.run();

expect(last([1, 2, 3])).toEqual(some(3));
});

it('should work with inputs of varying shapes', () => {
type Input = { type: 'test' } | ['hello', Option<string>] | 'hello'[];
type Output = ['hello', Option<string>];
const input = { type: 'test' } as Input;

const output = match(input)
.exhaustive()
.with(
['hello', { kind: 'some' }],
(x): Output => {
return x;
}
)
.with(['hello'], (x) => {
return ['hello', none];
})
.with({ type: __ }, () => ['hello', none])
.with([__], () => ['hello', none])
.run();
});
});
});

0 comments on commit 1f44061

Please sign in to comment.