2

I'm writing compiler from kind of functional language to JS. Compiler would run in browser. I need to implement pattern matching mechanics in JS, because original language have one. I've found Sparkler and Z. Sparkler can't be executed in browser as far as I know and Z doesn't have all possibilities I need.

So my language have semantics like this:

count x []         <- 0
count x [ x : xs ] <- 1 + count x xs
count x [ y : xs ] <- count x xs

This is what happens in this snippet:

First line is definition of a function, which takes two parameters: some variable x and empty list, and returns zero.

Second line is definition of a function, which also takes two parameters: some variable x and list, which starts with x, and returns 1 + count(x, xs)

Fot this example I want to generate code like this:

const count = (x, list) => {
    match(x, list) => (
        (x, []) => {...}
        (x, [ x : xs ]) => {...}
        (x, [ y : xs ]) => {...}
    )
} 

How properly unfold this kind of pattern matching into ifs and ors?

6
  • Do you only support pattern matching on certain built-in types or general algebraic data types? What types of patterns do you support? For example, do you support user-defined patterns (like extractors in Scala or active patterns in F#)? Is [x : xs] a linked list (that could be represented as an ADT) or an array (that'd presumably be represented as a JavaScript array)? If you support ADTs, do you already know how you'll represent them? Commented May 21, 2018 at 16:24
  • Linked list is represented as JS array. I need to support only single values for built-in types and list destructuring. Also no support for algebraic data types. Commented May 21, 2018 at 16:29
  • How do you represent a linked list as an array? Do you mean a two-element array where one element is the head and the other the tail? Or an array of objects where each object contains a value and the index of its tail in the array? Or was that a typo and you meant "Lists are arrays"? Anyway, if it's just constants, variables, tuples and arrays and/or lists, tuple patterns can just translate to assignments of the elements, constant patterns to switches and list/array patterns to ifs that check the size of the list and then assign the elements. Commented May 21, 2018 at 16:39
  • 1
    The repos you mentioned try to implement pattern matching in Javascript, which isn't possible, because real pattern matching doesn't depend on equality but on structure. You also cannot utilize destructuring assignment, because it has a different semantics. For instance, it throws an error when a mismatches occurs. Commented May 21, 2018 at 18:11
  • @sepp2k is there any article or something about method you proposed? I would like to have more details on it Commented May 21, 2018 at 21:05

3 Answers 3

3

General case

There is a proposal for Pattern Matching in ECMAScript, but as of 2018 it's in a very early stage.

Currently, the Implementations section only lists:

List case

Use destructuring assignment, like:

const count = list => {
  const [x, ...xs] = list;
  if (x === undefined) {
    return 0;
  } else if (xs === undefined) {
    return 1;
  } else {
    return 1 + count(xs);
  }
}
Sign up to request clarification or add additional context in comments.

Comments

1

I had a need for pattern matching and made something that works for me.

const count = patroon(
  [_], ([, ...xs]) => 1 + count(xs),
  [], 0
)

count([0,1,2,3])
4

See readme for more usage examples.

Comments

0

Using ex-patterns, you could write your example as follows. You need to use the placeholder names that come with the package (_, A, B, C, ... Z) but you can rename matched variables in the callback function with destructuring (an object containing all named matches is passed in as the first argument to the callback function).

import { when, then, Y, _, tail, end } from 'ex-patterns';

const count = list => (
    when(list)
        ([], then(() => 0))  // match empty array   
        ([_], then(() => 1))  // match array with (any) 1 element      
        ([_, tail(Y)], then(({ Y: xs }) => 1 + count(xs)))  // match array and capture tail              
    (end);
);

This also covers the case where list = [undefined, 'foo', 'bar'], which I don't think would be covered by the accepted answer.

To make the code more efficient, you can call count with an Immutable.js List instead of an array (no changes required). In that case, the tail portion of the array doesn't need to be sliced and copied into a new array on every loop.

As with the packages you mentioned, this doesn't run in the browser natively, but I guess that's not a major obstacle with modern bundling tools.

Here are the docs: https://moritzploss.github.io/ex-patterns/

Disclaimer: I'm the author of ex-patterns :)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.