3.1 Preparing for the pattern matching algorithm

A destructuring assignment looks like this:

  1. «pattern» = «value»

We want to use pattern to extract data from value.

We will now look at an algorithm for performing this kind of assignment. This algorithm is known in functional programming as pattern matching (short: matching). It specifies the operator (“match against”) that matches a pattern against a value and assigns to variables while doing so:

  1. «pattern» «value»

We will only explore destructuring assignment, but destructuring variable declarations and destructuring parameter definitions work similarly. We won’t go into advanced features, either: Computed property keys, property value shorthands, and object properties and array elements as assignment targets, are beyond the scope of this chapter.

The specification for the match operator consists of declarative rules that descend into the structures of both operands. The declarative notation may take some getting used to, but it makes the specification more concise.

3.1.1 Using declarative rules for specifying the matching algorithm

The declarative rules used in this chapter operate on input and produce the result of the algorithm via side effects. This is one such rule (which we’ll see again later):

  • (2c) {key: «pattern», «properties»} ← obj

    1. «pattern» obj.key
    2. properties»} obj

This rule has the following parts:

  • (2c) is the number of the rule. The number is used to refer to the rule.
  • The head (first line) describes what the input must look like so that this rule can be applied.
  • The body (remaining lines) describes what happens if the rule is applied.

In rule (2c), the head means that this rule can be applied if there is an object pattern with at least one property (whose key is key) and zero or more remaining properties. The effect of this rule is that execution continues with the property value pattern being matched against obj.key and the remaining properties being matched against obj.

Let’s consider one more rule from this chapter:

  • (2e) {} ← obj (no properties left)

    1. // We are finished

In rule (2e), the head means that this rule is executed if the empty object pattern {} is matched against a value obj. The body means that, in this case, we are done.

Together, rule (2c) and rule (2e) form a declarative loop that iterates over the properties of the pattern on the left-hand side of the arrow.

3.1.2 Evaluating expressions based on the declarative rules

The complete algorithm is specified via a sequence of declarative rules. Let’s assume we want to evaluate the following matching expression:

  1. {first: f, last: l} obj

To apply a sequence of rules, we go over them from top to bottom and execute the first applicable rule. If there is a matching expression in the body of that rule, the rules are applied again. And so on.

Sometimes the head includes a condition that also determines if a rule is applicable – for example:

  • (3a) [«elements»] ← non_iterable
    if (!isIterable(non_iterable))

    1. throw new TypeError();