From 9b2738497974cfe96874853d44d579a41fa8e885 Mon Sep 17 00:00:00 2001 From: gvergnaud Date: Tue, 27 Jun 2023 12:23:06 +0200 Subject: [PATCH] perf: runtime perf improvement in .with --- src/match.ts | 61 +++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/src/match.ts b/src/match.ts index 4ef3c9e6..3602de7f 100644 --- a/src/match.ts +++ b/src/match.ts @@ -3,6 +3,15 @@ import { Match } from './types/Match'; import * as symbols from './internals/symbols'; import { matchPattern } from './internals/helpers'; +type MatchState = + | { matched: true; value: output } + | { matched: false; value: undefined }; + +const unmatched: MatchState = { + matched: false, + value: undefined, +}; + /** * `match` creates a **pattern matching expression**. * * Use `.with(pattern, handler)` to pattern match on the input. @@ -22,18 +31,9 @@ import { matchPattern } from './internals/helpers'; export function match( value: input ): Match { - return new MatchExpression(value) as any; + return new MatchExpression(value, unmatched) as any; } -type MatchState = - | { matched: true; value: output } - | { matched: false; value: undefined }; - -const unmatched: MatchState = { - matched: false, - value: undefined, -}; - /** * This class represents a match expression. It follows the * builder pattern, we chain methods to add features to the expression @@ -44,10 +44,7 @@ const unmatched: MatchState = { * can be found in src/types/Match.ts. */ class MatchExpression { - constructor( - private input: input, - private state: MatchState = unmatched - ) {} + constructor(private input: input, private state: MatchState) {} with(...args: any[]): MatchExpression { if (this.state.matched) return this; @@ -56,38 +53,38 @@ class MatchExpression { args[args.length - 1]; const patterns: Pattern[] = [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 = {}; + 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;