• Stars
    star
    247
  • Rank 164,117 (Top 4 %)
  • Language
    JavaScript
  • License
    Other
  • Created over 12 years ago
  • Updated over 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An experiment in adding functional pattern matching to JavaScript

Functional Pattern Matching in JavaScript

Pattern matching is a form of conditional branching which allows you to concisely match on data structure patterns and bind variables at the same time ( Wikipedia). Pattern matching is supported in some functional languages such as ML, Haskell, OCaml, and Erlang. This library implements pattern matching for the JavaScript language in an efficient and concise way. The following example shows what pattern matching in JavaScript looks like:

var fact = fun(
    [0, () =>  1],
    [$, (n) => n * fact(n - 1)]
);

The above function implements a simple factorial function using pattern matching. When you call fact(10) the value ‘10’ is matched against the first pattern ‘0’. This match fails and the next pattern is evaluated. The ‘$’ in the next pattern is an example of a parameter. A parameter matches anything, so the match succeeds and ‘10’ is passed as an argument to the anonymous function. Since this is a recursive function it will match the second pattern until the argument to the function reaches zero and then terminates. Note that this example uses JavaScript ES6 syntax, code in previous JavaScript versions will be slightly more verbose.

Note: This is an experiment, do not use this in real code

Usage

To use the library install its npm package:

npm install funcy

And require it in your program:

var fun = require('funcy');

The fun method takes one or more pattern expressions and returns a function which, when called, will try to match the values passed to the patterns. If a match is found an anonymous function corresponding to the matched pattern is executed, and any extracted values passed to it as arguments.

A pattern expression is an array literal with a variable number of patterns, and an anonymous function as the last item of the array. Patterns are explained in more detail in the next section.

Patterns

A pattern is one of the following types: atom, RegExp, Object, Array, Function, or the special wildcard and parameter patterns. The wildcard pattern matches against any value, but does not return anything. The parameter pattern on the other hand returns the value it was matched against, and also matches against any value.

Atom
Atoms match against values that are strictly equal. No type conversion is performed. Atoms are any of the following JavaScript types (or values): `Number`, `String`, `Boolean`, `null`, `undefined`, `NaN`, and `Infinity`.
RegExp
RegExp match against values that are string. Uses regExpPattern.test(value) to match.
Object
Objects match against values that are objects, of the same type (determined by the `object.constructor` property), have the same number of properties with all keys and values being strictly equal. The order in which the properties are declared is not important. Property names cannot contain wildcards.
Array
Arrays match against values that are arrays, have the same number of elements with all elements being strictly equal and in the same order as the pattern array.
Parameter
Parameter matches against a value of any type, and returns that value as an argument to the anonymous function in the pattern expression. The values are given in the order the parameters are defined in the pattern(s).
Wildcard
Wildcard matches against a value of any type, but does not return that value.
Function
Functions match against values that are of the same type (determined by `function.constructor`.) If a match is successful the value is returned. `Parameter` and `Wildcard` are special instances of the `Function` pattern which do not check if the value and pattern are of the same type.

The following are all valid pattern expressions with an empty anonymous function.

// matches 1
[1, function () {}]

// matches /ok$/ regular expression
[/ok$/,function(){}]

// matches {key: 'value'}
[{key: 'value'}, function () {}]

// matches the array [3, true]
[[3, true], function () {}]

// matches {key: x} and passes x as an argument to the anonymous function
[{key: fun.parameter}, function (x) {}]

// matches everything
[fun.wildcard, function () {}]

The second last example shows how parameters can be passed to the anonymous functions, in essence extracting data from the value the pattern was matched against. Note that the parameters do not create named variables, but return their values in the order the parameters were declared. This means that the following example will bind the x property to the y argument, and the y property to the x argument in the anonymous function.

[{x: $, y: $}, function (y, x) {}]

API

The library exposes one method usually called fun, which takes one or more pattern expressions as arguments. The method has two additional properties, one for the wildcard pattern and one for the parameter pattern.

fun( pattern_expression1, pattern_expression2, ...)
Creates a new function which performs pattern matching using the patterns in the pattern expressions, and executes their associated anonymous function if a match is made. Pattern expressions are tried for a potential match in the order they are declared. Throws an exception if none of the patterns in the expressions match.
fun.wildcard
The wildcard pattern can be used to ‘mask out’ or ignore certain parts of the value it is matched against.
<dt>fun.parameter</dt>
<dd>The parameter pattern can be used to bind values to variables which are then passed as arguments to the anonymous function. The arguments are given in the same order as they were defined in the pattern(s).</dd>

Pattern expressions are preprocessed (any operation that can be performed without knowledge of the value it will be matched against) so that the run-time costs of code using pattern matching is kept at a minimum. The pattern matcher also prioritizes match tests that are fast before performing more expensive tests, in order to find a suitable match as soon as possible.

Examples

To keep my code (and these examples) concise I usually assign the special parameter and wildcard patterns to single character variable names. These are $ for the parameter pattern and _ for the wildcard pattern. The library does not define these by default in order to avoid conflicts with other libraries that use these variable names.

var $ = fun.parameter;
var _ = fun.wildcard;

Using these variables we can implement a simple (but inefficient) factorial function.

var fact = fun(
    [0, function () { return 1; }],
    [$, function (n) { return n * fact(n - 1); }]
);

In JavaScript ES6 it is possible to use a shorthand arrow functions, so the above factorial function can be rewritten as:

var fact = fun(
    [0, () => 1],
    [$, (n) => n * fact(n - 1)]
);

Another common use of pattern matching is to determine if a value is of a certain type and perform an action depending on the result. For example, let's say we have a print function which logs its value to the console. We would however like to customize the output for some data types. We can accomplish this using pattern matching as follows:

var print = fun(
    // match and return Date values
    [Date, function (d) { ... }],

    // match and return String values
    [String, function (str) { ... }],

    // match and return any other type
    [$, function (o) { ... }]
);

If the type of the value is Date, the first anonymous function will be executed and its value passed as argument. The same applies to values of type String. Any other value will be passed to the last anonymous function whose pattern acts as a catch-all.

More Repositories

1

fontfaceobserver

Webfont loading. Simple, small, and efficient.
JavaScript
4,235
star
2

typeset

TeX line breaking algorithm in JavaScript
JavaScript
980
star
3

hypher

A fast and small JavaScript hyphenation engine
JavaScript
564
star
4

trmix

apply CSS based on your browser's text rendering engine
JavaScript
500
star
5

homebrew-webfonttools

Homebrew formulae for font tools
Ruby
359
star
6

fontloader

A fontloader polyfill
JavaScript
324
star
7

xsltjson

XSLTJSON - Convert XML to JSON using XSLT
XSLT
305
star
8

jlayout

JavaScript layout algorithms
JavaScript
283
star
9

url-template

A JavaScript URI template implementation (RFC 6570 compliant)
JavaScript
179
star
10

opentype

An OpenType, TrueType, WOFF, and WOFF2 parser in JavaScript
JavaScript
133
star
11

sfnt2woff-zopfli

WOFF utilities with Zopfli compression
C
126
star
12

promis

A small embeddable Promise polyfill
JavaScript
97
star
13

postcss-scale

PostCSS plugin to scale values from one range to another.
HTML
80
star
14

bit-array

JavaScript implementation of bit arrays.
JavaScript
78
star
15

hyphenation-patterns

Hyphenation patterns for use with Hypher
JavaScript
74
star
16

stateofwebtype

Up-to-date data on support for type and typographic features on the web.
JavaScript
65
star
17

junify

JUnify ― JavaScript Unification Library
JavaScript
48
star
18

text-overflow

jQuery Text Overflow plugin
JavaScript
43
star
19

jsizes

jQuery CSS size properties plugin
JavaScript
37
star
20

characterset

A library for creating and manipulating character sets
JavaScript
29
star
21

css-font-parser

A parser for the CSS font values
JavaScript
26
star
22

jslint

JSLint: The JavaScript Quality Tool, command line version (Node.js)
JavaScript
25
star
23

datrie

A JavaScript Double Array Trie
JavaScript
21
star
24

unicode-tokenizer

Unicode Tokenizer following the Unicode Line Breaking algorithm
JavaScript
20
star
25

nanofont

A nano font for testing font format support
Makefile
19
star
26

node-typekit

A minimal Typekit API client in Node.js
JavaScript
19
star
27

knockout.selection

A selection binding for Knockout.js
JavaScript
19
star
28

javascript

Various JavaScript projects & tools.
JavaScript
17
star
29

knockout.dragdrop

A HTML5 drag and drop binding for Knockout.
JavaScript
16
star
30

text-align

jQuery Text Alignment plugin
JavaScript
13
star
31

tpo

Next generation of browser typesetting
JavaScript
13
star
32

closure-compiler-inline

A Closure Compiler fork with more control over function inlining
Java
11
star
33

calcdeps

A Node.js port of Google Closure library calcdeps.py
JavaScript
11
star
34

js-preprocess

JavaScript Preprocessor
JavaScript
9
star
35

column-selector

jQuery Column Selector
JavaScript
9
star
36

fonzie

A tiny @font-face loader
JavaScript
8
star
37

phantomjs-typekit

A simple demo of using Typekit with PhantomJS
JavaScript
8
star
38

epub2ts

ePub to Treesaver conversion
JavaScript
8
star
39

php-typekit

A PHP client for the Typekit API
PHP
7
star
40

shp2json

Simple tool to convert Shapefiles (GIS) to JSON
JavaScript
6
star
41

hyphenation-justification-vf

JavaScript
5
star
42

nanoserver

A simple web server for development
JavaScript
5
star
43

emfont

A font with a single character filling the entire em-box
HTML
5
star
44

ui

C++/OpenGL User Interface library
5
star
45

jslint-core

JSLint: The JavaScript Code Quality Tool packaged as a CommonJS module
JavaScript
5
star
46

mocha-browserstack

A Mocha reporter that can be used to run Mocha tests automatically on BrowserStack
JavaScript
4
star
47

sfnt2woff

C
4
star
48

node-browserstack

A Node.js client for the BrowserStack API (v3 and screenshot)
JavaScript
4
star
49

website

bramstein.com website source
JavaScript
3
star
50

unicode-data-parser

JavaScript
3
star
51

markup

JavaScript
2
star
52

closure-dom

JavaScript
2
star
53

ui-test

C++/OpenGL User Interface library test project
2
star
54

closureloader

Load code using the Closure library dependency syntax in Node.js
JavaScript
2
star
55

cssvalue

Parsers (and generators) for common CSS values.
JavaScript
2
star
56

thesis

Master Thesis: "Visualizations on the Web"
2
star
57

sowt-test

Automated browser tests for State of Web Type
JavaScript
2
star
58

closure-fetch

JavaScript
1
star
59

detect-writing-script

Detect the writing script given an array of codepoints.
JavaScript
1
star
60

ui-demo

C++/OpenGL User Interface library demo
1
star
61

font-weight-test

Test case for font-weight fallback behaviour
Makefile
1
star
62

amd-to-closure

Transform AMD modules to Closure Compiler dependencies
JavaScript
1
star
63

fontformatdetection

Detect browser support for font formats using feature detection
JavaScript
1
star