Skip to content

Commit

Permalink
perf: runtime perf improvement in .with
Browse files Browse the repository at this point in the history
  • Loading branch information
gvergnaud committed Jun 27, 2023
1 parent 4b96fe1 commit 9b27384
Showing 1 changed file with 29 additions and 32 deletions.
61 changes: 29 additions & 32 deletions src/match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ import { Match } from './types/Match';
import * as symbols from './internals/symbols';
import { matchPattern } from './internals/helpers';

type MatchState<output> =
| { matched: true; value: output }
| { matched: false; value: undefined };

const unmatched: MatchState<never> = {
matched: false,
value: undefined,
};

/**
* `match` creates a **pattern matching expression**.
* * Use `.with(pattern, handler)` to pattern match on the input.
Expand All @@ -22,18 +31,9 @@ import { matchPattern } from './internals/helpers';
export function match<const input, output = symbols.unset>(
value: input
): Match<input, output> {
return new MatchExpression(value) as any;
return new MatchExpression(value, unmatched) as any;
}

type MatchState<output> =
| { matched: true; value: output }
| { matched: false; value: undefined };

const unmatched: MatchState<never> = {
matched: false,
value: undefined,
};

/**
* This class represents a match expression. It follows the
* builder pattern, we chain methods to add features to the expression
Expand All @@ -44,10 +44,7 @@ const unmatched: MatchState<never> = {
* can be found in src/types/Match.ts.
*/
class MatchExpression<input, output> {
constructor(
private input: input,
private state: MatchState<output> = unmatched
) {}
constructor(private input: input, private state: MatchState<output>) {}

with(...args: any[]): MatchExpression<input, output> {
if (this.state.matched) return this;
Expand All @@ -56,38 +53,38 @@ class MatchExpression<input, output> {
args[args.length - 1];

const patterns: Pattern<input>[] = [args[0]];
const predicates: ((value: input) => unknown)[] = [];
let predicate: ((value: input) => unknown) | undefined = undefined;

// case with guard as second argument
if (args.length === 3 && typeof args[1] === 'function') {
// case with guard as second argument
patterns.push(args[0]);
predicates.push(args[1]);
predicate = args[1];
} else if (args.length > 2) {
// case with several patterns
patterns.push(...args.slice(1, args.length - 1));
}

let hasSelections = false;
let selected: Record<string, unknown> = {};
const select = (key: string, value: unknown) => {
hasSelections = true;
selected[key] = value;
};

const matched = Boolean(
patterns.some((pattern) =>
matchPattern(pattern, this.input, (key, value) => {
selected[key] = value;
})
) && predicates.every((predicate) => predicate(this.input))
);
const matched =
patterns.some((pattern) => matchPattern(pattern, this.input, select)) &&
(predicate ? Boolean(predicate(this.input)) : true);

const selections = hasSelections
? symbols.anonymousSelectKey in selected
? selected[symbols.anonymousSelectKey]
: selected
: this.input;

const state = matched
? {
matched: true as const,
value: handler(
Object.keys(selected).length
? symbols.anonymousSelectKey in selected
? selected[symbols.anonymousSelectKey]
: selected
: this.input,
this.input
),
value: handler(selections, this.input),
}
: unmatched;

Expand Down

0 comments on commit 9b27384

Please sign in to comment.