Assignment 8: Pattern Matching (an internal DSL)
In Assignment 8 of this class, we will “prototype” an internal domain-specific language for pattern matching in JavaScript.
Introduction

Object-oriented languages (including JavaScript) have primitive support for dynamic dispatch, which automatically chooses the best method to invoke at run time, based on the class of the receiver. You made use of this feature to good effect in the Interpreter assignments. For example, the eval You may have used a different name, e.g., interp. method of a BinOp AST node can simply invoke this.e1.eval(…) to recursively evaluate its first operand, without having to manually figure out the right eval method implementation for it; rather, each kind of node just “knows” how to evaluate itself.

Including the language you implemented in Assignment 6. However, dynamic dispatch has some important limitations when compared to a general mechanism for pattern matching, as supported by many functional languages:

Of course, one solution to this problem is to simply switch to Ha language that already supports pattern matching, like the one you implemented in Assignment 6. But changing languages is not always an option:

An increasingly popular solution to this problem is to provide the desired abstraction in the form of an internal domain-specific language (DSL). This name is important, so let’s break it down:

In other words, an internal DSL extends an existing language with new features that provide increased expressiveness, security, performance, etc.

Internal DSLs (usually) don’t have their own syntax; instead they have a carefully-designed API in the host language that feels somewhat natural to write, and makes for readable code. Designing such an API is not easy, and some host languages are better than others. Smalltalk, Ruby, Haskell, and Scala are all reasonably good host languages. C not so much, though its macro system can be used to implement some limited kinds of DSLs. Java is basically awful as a host language.

So what should you look for in a host language?

Assignment 8: An Internal DSL for Pattern Matching Due Thursday, March 5th, at 11:59pm
Turn in your modified match.js on Canvas, as well as an updated PDF with your Concept-based language design.

Note: if you didn't lose any points in Assignment 2, you are not required to do Part I (I think this only applies to one project in the class). For Part I of Assignment 8, you will revise your concept-based language design from Assignment 2 to fix any issues identified in the feedback you received. You will have a chance to regain any points you lost in Assignment 2.

For Part II of Assignment 8, you’ll implement an internal DSL for pattern matching in JavaScript. Here’s an example of how this DSL could be used to implement a zip function, which turns two lists into a list of pairs, assuming we have declared classes Nil and Cons to implement linked lists: function zip(l1, l2) { return match([l1, l2], [instof(Nil), instof(Nil)], () => new Nil(), [instof(Cons,_,_), instof(Cons,_,_)], (x, xs, y, ys) => new Cons([x,y], zip(xs,ys)) ); } Pattern matching with this DSL may not be as nice as doing it in a language that has native support for pattern matching, but it sure beats deconstructing the objects manually. The resulting code is both more readable (once you get used to the syntax) and less error-prone.

Description of the DSL

The match function

Our DSL consists of a match function whose arguments are a value to be matched, followed by zero or more (pattern, function) pairs: match(value, pat1, fun1, pat2, fun2, …) match will try to match the value with each of the patterns, in left-to-right order. When it finds the first pattern that matches the value, match will call that pattern’s corresponding function and return the result of that call. If none of the patterns match the value, match will throw an exception like this: throw new Error('match failed');

Patterns

A pattern in our DSL serves two purposes:

Here is a list of the patterns that are supported in our DSL:

Keep in mind that match is just a regular JavaScript function, which means that its arguments will be evaluated before any pattern matching actually happens. This includes patterns like many(pred(isNumber)): they are plain old JavaScript expressions that will be evaluated to values before entering the match function.

Getting Started, Unit Tests, etc.

Please do your work in a file called match.js. Each time you refresh this page, that file is loaded by our test harness to run the unit tests below. As in the previous assignments, you can add your own test cases by editing tests.js.

Playground
Epilogue

If you’re interested in pushing this project further, here are a few things you might like to try:

Recommended Reading