npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@davidisaaclee/matcher

v0.1.4

Published

A tiny library for creating pattern matching functions over custom types in vanilla Javascript.

Downloads

17

Readme

matcher

A tiny library for creating pattern matching functions over custom types in vanilla Javascript.

Good for:

  • Defining methods for enum-like types that switch their behavior based on their input.
  • Getting something like type safety by being notified of functions which omit registered cases or include invalid cases for their input.
  • Referring to the names of your enumeration's cases without worrying about typos.
  • Defining functions that switch on a custom partitioning of its input set. Define one matcher to partition numbers into even/odd; another to partition into positive/nonpositive; another to partition into the temperature ranges for the different phases of water in degrees celsius...

Probably not good for:

  • Quickly making throwaway anonymous functions. Creating pattern-matching functions with this library is optimized for giving good error messages at function definition, not for creating functions as quickly as possible.
  • Making functions for a type that can be added to externally. Once a matcher is created, cases can't be added to it. If you want users of your code to be able to add new cases to your type, you should probably define behavior inside your type (e.g via class methods).

Usage

  1. Install this package. This package is listed on npm as @davidisaaclee/matcher. To install with npm, run the following command:
$ npm install @davidisaaclee/matcher
  1. Import createMatcher from this package.
// ES6 module
import createMatcher from '@davidisaaclee/matcher';

// NodeJS - note the required `.default`
const createMatcher = require('@davidisaaclee/matcher').default;
  1. Create a pattern-matching generator by calling createMatcher. In this call, we'll need to provide a set of case names, which are string indices for the different "slots" that your input can map to. In an even/odd matcher, the cases would be named "even" and "odd". We'll also need to provide a way of partitioning an input into those named cases. In an even/odd matcher, this would be a function mapping a number to either even or odd.
const evenOddMatcher = createMatcher(
	// Register our two cases by name.
	['even', 'odd'],
	// Define how to map an integer into the even or odd cases.
	// A `cases` function is provided to us to use here. We can call it with our
	// case names (the strings "even" or "odd") to reference the cases we just defined.
	// Attempting to reference a case that wasn't registered will throw an error.
	cases => integer => {
		if (integer % 2 === 0) {
			return cases('even');
		} else {
			return cases('odd');
		}
	});
  1. Generate your function by invoking that generator with your pattern-matching behavior.
// Here's a function that picks an integer near the midpoint between 0 and some endpoint.
// Once again, we're provided a `cases` function for referencing our previously-defined cases by name.
const integralMidpoint = evenOddMatcher(cases => ({
	[cases('even')]: number => number / 2,
	[cases('odd')]: number => (number + 1) / 2,
}));
  1. Use that function.
integralMidpoint(4); // => 2
integralMidpoint(5); // => 3

Example: Matching sign of numbers

const signMatcher = createMatcher(
	['positive', 'negative', 'zero'],
	cases => n => {
		if (n > 0) {
			return cases('positive');
		} else if (n < 0) {
			return cases('negative');
		} else {
			return cases('zero');
		}
	});

const absoluteValue = signMatcher(cases => ({
	[cases('positive')]: n => n,
	[cases('negative')]: n => -n,
	[cases('zero')]: n => 0,
	// [cases('undefined case')]: n => null, // would throw an error when absoluteValue is created
}));

Example: Autogenerating a matcher from an enumerated type

// Every shape will have a property `type` with one of these values.
const shapeTypes = {
	'square': 'square',
	'circle': 'circle',
};

const makeSquare = (sideLength) => ({
	type: shapeTypes.square,
	sideLength
});

const makeCircle = (radius) => ({
	type: shapeTypes.circle,
	radius
});

const shapeTypeMatcher = createMatcher(
	Object.values(shapeTypes),
	cases => shape => cases(shape.type));

const area = shapeTypeMatcher(cases => ({
	[cases(shapeTypes.square)]: (shape) => shape.sideLength * shape.sideLength,
	[cases(shapeTypes.circle)]: (shape) => shape.radius * shape.radius * Math.PI,
}));

Example: Creating a matcher in ES5

Here's the same even-odd matcher and generated integralMidpoint function as defined in Usage, written using ES5.

var evenOddMatcher = createMatcher(
	['even', 'odd'],
	function (cases) {
		return function (integer) {
			if (integer % 2 === 0) {
				return cases('even');
			} else {
				return cases('odd');
			}
		};
	});

var integralMidpoint = evenOddMatcher(function (cases) {
	var indexer = {};

	indexer[cases('even')] = function (number) {
		return number / 2;
	};
	indexer[cases('odd')] = function (number) {
		return (number + 1) / 2;
	};

	return indexer;
});

Similar projects

  • z-pattern-matching - This is a really neat implementation of pattern matching that relies on reflection to make function definition super lightweight. While this is cool, I didn't like how "magical" it felt. I also wanted to easily match my custom types, instead of repeatedly defining how I wanted to partition the input into its cases.
  • sparkler - A pattern-matching engine powered by sweet.js macros. This seems well-made, and has a lot of nice features. sweet.js seems sweet, but I wanted a very vanilla solution to this very vanilla problem.
  • official ECMAScript pattern matching proposal - In stage 0. Hope to see this happen someday...