Skip to content

NEP8 Pattern Matching

Greg Hewgill edited this page Jul 27, 2019 · 3 revisions

This proposal introduces pattern matching to provide a shortcut way to deconstruct compound values.

Motivation

Consider a fragment of code that needs to inspect an array, and extract the second element if the first element is 1. Without pattern matching, this might be written as follows:

VAR a: Array<Number>
% ... populate a
IF a.size() >= 2 AND a[0] == 1 THEN
    LET x: Number := a[1]
    % ... process x
END IF

This uses a compound condition to test the first element and requires a second LET statement to extract x. With pattern matching, this could be written as:

VAR a: Array<Number>
% ... populate a
IF MATCH [1, x, ...] := a THEN
    % ... process x
END IF

Pattern Structure

A pattern is a new grammatical construct that looks like a literal value, but with a different structure. The following things can be match targets:

  • A constant value, to match that value exactly.
  • An identifier, to capture a value and make the value available under the given name in the following scope.
  • Another pattern value (to support nested patterns).
  • An underscore, as a placeholder to match anything but ignore its value.

Match Patterns

  • Arrays: Arrays perhaps provide the most flexibility in matching. Not only can any element be matched, but a subarray may be extracted as well using ... notation. Examples:

    • [x] - match an array with exactly one element and extract the value into x
    • [x, a...] - extract the first element into x, and the remainder of the array (which may be empty) into a
    • [x, ...] - extract the first element into x, but the array may contain more elements (which are not extracted)
    • [..., z] - extract the last element into z
    • [x, y, a..., z] - extract the first element into x, the second into y, the last into x, and the remainder between them into a

    There may not be more than one ... in an array pattern.

  • Dictionaries: Dictionaries can be matched on values of specific keys but there is no provision for extracting a subdictionary. Examples:

    • {"foo": x} - match a dictionary with exactly one key foo, and extract the value into x
    • {"foo": x, ...} match a dictionary with any number of keys, one of them being foo
  • Records: Records can be pattern matched using their constructor syntax:

    • Rec(foo WITH x) - match any record of type Rec and extract the value of field foo into x
  • Interface pointers: Polymorphic classes can be matched against their actual type using the following syntax:

    • POINTER TO Cls(foo WITH x) - match a pointer to an instance of Cls, extract field foo as x