More control over how a pattern is matched

After I retired, structural pattern matching was the feature that encouraged me to upgrade to 3.10. But once I started to play with it, I was disappointed. Why could I not have ‘a * math.sin(b)’ in a case statement? Why could I only have one sub-sequence, and not match [3, 4] from [1, 2, 3, 4, 5, 6, …]? As time has passed and I have continued to us it, I’ve been impressed.

There are probably many enhancements to structural pattern matching that could be made in the future and I would like to propose that one of them allows programmers to explicitly control the pattern matching, i.e. the case statement can call a user defined function/class passing the value of the variable to be matched and a string or ast tree describing the pattern. The code below shows what this could look like and it has been tested using a simple preprocessor.

#!python3.10
import pattern

 ass = [1, 2, 3, 4, 5, 6, 7, 8 , 9, 'a', 'b', 'c', 'd']

for var in [ass, 5, 2, 'axxd', 'ab']:
    print(F'\nTesting {var}')
   # Note that the following code causes fatal syntax errors during compilation
   match var with pattern.sequence: # (a), refers to the notes below
        case [~2 as x, *y, ~5 as z]: # uses pattern.sequence
            print(F'Interior', {x}, {y}, {z})
        case 5 >= Y > 2 with pattern.logic as Y : # (b)
            print(F"pattern {Y}")
        case 5 with pattern.value as FRED: # 
            print(F"pattern.value with {FRED}")
        case 'ab' with pattern.Null as  AB: # (c)
            print(F'match {AB}')
        case r'a..d' with pattern.RegExp as REG: # (d)
            print(F'match {REG}')
        case _:
            print("No match")
  1. The match statement is optionally enhanced, with specifying the name of the pattern matching code. This would be used for every case statement unless overridden.
  2. The case statement is optionally enhanced, with specifying an overriding pattern matcher and as naming the variable to bind.
  3. If no match or an internal error occurs the match function returns None, anything else signifies a match.

So currently
case [~2 as x, *y, ~5 as z]:
becomes
case dnx0 if pattern.sequence(dnx0, "~2 >> x, *y, ~5 >> z”) is not None:

I’ve implemented pattern matchers for
a) enhanced sequent unpacking for finite sequences, (~3 means return the value of list.index(3)) and sets a placeholder. as renamed to >> so the pattern is a legal ast tree,
b) matching logic expressions,
c) matching against a general expression, number or string, revert to builtin.
d) matching against regular expressions.

This is just syntactic sugar like pattern matching, but I think it looks better than lots of guards or if/else chains.

1 Like

Please change the title to refect the idea you are discussing.