Skip to content

Latest commit

 

History

History
61 lines (45 loc) · 2.57 KB

1. Pattern Matching Protocol.md

File metadata and controls

61 lines (45 loc) · 2.57 KB

Pattern Matching Protocol

The library exposes the @@extractor and @@ignored symbols which are used in the pattern matching protocol.

An object that implements pattern matching has a @@extractor function which takes in two arguments, the value of the thing to match, and previousExtracted which is an object containing any previous extracted values.
It must return an object with a property matched containing a boolean on whether a match was made and if it did, a property extracted containing an object of extracted values.

The object can also have a boolean @@ignored property.
This indicates that the pattern matching will ignore the value to match with, allowing for some optimizations.

The library also contains a Pattern abstract class in the patterns object for extending, containing utility methods.
When pattern constructors expect a pattern to be passed in, Pattern.patternOf should be ran to ensure it has @@extractor.
The method patternOf takes in a value, and if it has @@extractor, return it, otherwise:

  • If the value is primitive, wrap it in an EqualPattern.
  • If the value is an array, pass it to ArrayPattern.
  • If the value is a Map, pass it to MapPattern.
  • Otherwise, pass it to ObjectPattern.

For the last three cases, if the structure has the @@rest symbol in it, it will be used as the rest pattern:

  • For arrays, the last item must be [@@rest, pattern].
  • For Maps, there must be an entry @@rest => pattern.
  • For objects, there must be an entry [@@rest]: pattern.

Each branch of patternOf has corresponding patternOfPrimitive, patternOfArray, patternOfMap, and patternOfObject methods.
The Pattern class also has the methods test and match corresponding to the built-in tester and matcher functions.

Example of a custom pattern:

class NotEqualAndBindPattern extends Pattern {
    constructor(value, id) {
        super();
        this.value = value;
        this.id = id;
    }

    [extractor](value, previousExtracted) {
        if (this.id in previousExtracted && !Object.is(value, previousExtracted[this.id])) {
            return { matched: false };
        }

        return !Object.is(value, this.value)
            ? { matched: true, extracted: { [this.id]: value } }
            : { matched: false };
    }
}

// This object implements the protocol.
const pattern = new NotEqualAndBindPattern(10, 'x');

pattern[extractor](10, {})
 { matched: false }

pattern[extractor](11, {})
 { matched: true, extracted: { x: 11 } }

pattern[extractor](11, { x: 5 })
 { matched: false }