From 794cd3f120622f2d7f217a443c0b7d7739f0762b Mon Sep 17 00:00:00 2001 From: Joeri Exelmans Date: Tue, 17 Jun 2025 11:12:34 +0200 Subject: [PATCH] nicer pattern matching --- lib/structures/enum.js | 19 +++++++++++++++++++ tests/enum.js | 11 +++++++++++ 2 files changed, 30 insertions(+) diff --git a/lib/structures/enum.js b/lib/structures/enum.js index d2276f1..9bdb0b3 100644 --- a/lib/structures/enum.js +++ b/lib/structures/enum.js @@ -1,4 +1,5 @@ import { compareStrings } from "../compare/primitives.js"; +import { unit } from "../primitives/unit.js"; import { capitalizeFirstLetter } from "../util/util.js"; const eatParameters = (numParams, result) => { @@ -33,6 +34,24 @@ const matchFn = (enumm, variantNames) => { }; }; +export const symbolElse = Symbol('else'); + +export const makeNiceMatchFn = (variantNames) => handlers => enumm => { + const incomplete = variantNames.filter(({l: variant}) => !handlers.hasOwnProperty(variant)).length > 0; + if (incomplete && !handlers.hasOwnProperty(symbolElse)) { + throw new Error("if not every variant is handled, must have 'else' handler"); + } + if (!incomplete && handlers.hasOwnProperty(symbolElse)) { + console.warn("'else' handler will never be invoked because every variant is already handled"); + } + for (const [variant, handler] of Object.entries(handlers)) { + if (enumm.variant === variant) { + return handler(enumm.value); + } + } + return handlers[symbolElse](unit); +} + export const makeConstructors = variantNames => { if (new Set(variantNames).size !== variantNames.length) { throw new Error("precondition failed: non-unique variant names"); diff --git a/tests/enum.js b/tests/enum.js index 8a8a71b..60c3311 100644 --- a/tests/enum.js +++ b/tests/enum.js @@ -1,6 +1,7 @@ import { makeTypeConstructor } from "../lib/meta/type_constructor.js"; import { Char, Double, Unit } from "../lib/primitives/primitive_types.js"; import { unit } from "../lib/primitives/unit.js"; +import { makeNiceMatchFn, symbolElse } from "../lib/structures/enum.js"; import { makeModuleEnum } from "../lib/structures/enum.types.js"; import { newProduct } from "../lib/structures/product.js"; import { lsType } from "../lib/structures/type_constructors.types.js"; @@ -44,3 +45,13 @@ const description = symptom => matchSymptom(symptom) assert.strictEqual("Fever (38.5 degrees)", description(fever)); assert.strictEqual("Confused" , description(confused)); assert.strictEqual("Other: sweating" , description(other)); + +// looks almost the same, but no currying, and the ability to have 'else' +const description2 = makeNiceMatchFn(variants)({ + fever: fever => `Fever (${fever} degrees)`, + [symbolElse]: _ => `Something else`, +}); + +assert.strictEqual("Fever (38.5 degrees)", description2(fever)); +assert.strictEqual("Something else" , description2(confused)); +assert.strictEqual("Something else" , description2(other));