Cody Shepp

Twitter | Github | LinkedIn

Kasai - Pattern Matching in TypeScript

Published on 9/26/2015

Pattern matching has become one of my favorite language constructs. Whenever I'm working in a language that doesn't have native pattern matching support, I really start to miss its powerful, concise syntax.

Recently I've been spending the majority of my time writing applications in TypeScript, so I decided to put together a small library that adds pattern matching to the list of other great features that TypeScript offers.

Kasai

When I wrote Kasai, I wanted to focus on two main benefits of pattern matching - (1) logic as data, and (2) concise inspection of complex data structures.

Logic as data

Representing logic as data allows you to take something like this:


var f;
var a = "1";

if(isString(a)){
    f = compareStrings;
}
else if(isNumber(a)){
    f = compareNumbers;
}
else if(isArray(a)){
    f = compareArrays;
}
else if(isObject(a){
    f = compareObject;
});

return f(a);

...and represent it with an array of guard/value pairs like this:


var a = "1";
var pattern = [
    [isString, (x) => compareStrings],
    [isNumber, (x) => compareNumbers],
    [isArray,  (x) => compareArrays ],
    [isObject, (x) => compareObjects]
];

return match(a, pattern);

The pattern matching syntax is not only shorter, but the patterns can also be created dynamically at runtime.

Also note that you can provide a function as a pattern - Kasai will pass the value you're matching against to that function, and if the result is true, that pattern will be a match.

You can also pass a function as the result of a match, in which case the value you are matching against will be applied to that function before it is returned. Because of this, if you want to return an unevaluated function from a match set, you'll need to create a little lambda like I did in the example above, which just throws away the input value and returns the function instead.

Inspection of complex data structures

How many times have you written code like this?


var a = {
    name: 'Cody',
    contactInformation: {
        telephone: '123-456-7890',
        address: {
            street: '123 abc st',
            city: 'Nowhere',
            state: 'XX',
            zip: '12345'
        }
    }
};

if(a.hasOwnProperty('contactInformation')
  && a.contactInformation.hasOwnProperty('address')
  && a.contactInformation.address.hasOwnProperty('zip')
  && a.contactInformation.addess.zip.length === 5)
{

    return "domestic";
}

return "foreign";

Yuck, yuck, yuck.

With Kasai, that mess turns into this:


return match(a, [
    [{contactInformation: {address: { zip: (z) => z.length === 5 }}}, 'domestic'],
    [_, 'foreign']
]);

Note that _ is a wildcard token that matches anything.

If you'd like to check out Kasai, you can find it on Github.