A case study on destructuring and pattern matching.
Switch branches/tags
Nothing to show
Latest commit d51af01 Jul 12, 2013 @eborden Update README.md
Permalink
Failed to load latest commit information.
README.md Update README.md Jul 12, 2013

README.md

Destructuring is one of the most powerful features of ES6. It allows for painless assignment of variables, but it could be so much more.

Destructuring is pattern matching. You can see this in one of the most shopped around examples of destructuring, flipping context.

var a = 1,
    b = 2;
console.log([b, a] = [a, b]);

Inherently this operation is matching a pattern.

In spyder monkey (one of the few engines supporting destructuring) it returns the matched array. This can be a useful tool.

Consider the following statement:

if (i = true) {
  console.log('this passes');
}

The implicit value is obvious in left hand assignment. It is the value of i. With a destructured statement the value is less implicit, but logical to infer.

console.log([a, b] = [1, 2]);// [1, 2]
console.log([a, b] = [1]);// [1]

Now consider the following:

([a, b] = [1, 2]).length;// true, all values have been fufilled
([a, b] = [1]).length == 2;// false, all values have not been fufilled

Rest parameters get a bit more complicated, since a rest parameter will return an empty array when no value is present:

console.log([a, ...rest] = [1, 2]);// [1, [2]]
console.log([a, ...rest] = [1]);// [1, []]

Sadly length fails to provide any significant test of pattern fufillment. length again fails in the case of arrays with undefined values.

var x = [];
x[0] = 1;
x[2] = 3;
([a, b, c] = x).length;// 3

With this in mind a testing operator needs to be implemented, so we can get the full power of pattern matching. I propose the ?= operator as an existential pattern matcher.

[a, b] ?= [1, 2];// true
[a, b] ?= [1];// false
[a, ...rest] ?= [1, 2];// true
[a, ...rest] = [1];// false
var x = [];
x[0] = 1;
x[2] = 3;
[a, b, c] ?= x;// false

?= providing let style assignment would further its usefulness.

if ([x, ...rest] ?= [1, 2, 3, 4]) {
  console.log(rest);
}

In this context rest parameters could be expanded to include an unassigned rest.

[a, ...] ?= [1, 2];// true
[a, ...] ?= [1];// false

With destructured pattern matching (and tail call optimization) we could implement elegant recursive functions.

function reverse (a) {
  if (a.length < 1) {
     return [];
  } else if ([x, ...rest] ?= a) {
     return reverse(rest).concat(x);
  }
}

I have excluded the use of object destructuring for simplicity, but the concept easily transfers:

if ({parent: {child: {name: n}}} ?= obj) {
  console.log('child has a name');
}