[Python-ideas] ML Style Pattern Matching for Python

Terry Reedy tjreedy at udel.edu
Sat Dec 18 01:14:07 CET 2010


On 12/17/2010 6:21 PM, Eike Welk wrote:
> After learning a bit of Ocaml I started to like its pattern matching features.
> Since then I want to have a "match" statement in Python. I wonder if anybody
> else would like this too.

-1 on adding anything like this to core Python.
In your example, it is
1. completely redundant with respect to current Python syntax;
2. look like chicken scratches;
3. limited by the chars availables to just a few of the infinity of 
tests one might want to run.
4. gives special prominence to tuples, which is hardly appropriate to 
list or iterable-oriented code.

The third point is why Python does not try to everything with syntax 
symbols. How test that the input is a positive number?How test that the 
input is a positive number? Type testing is somewhat contrary to 
duck-typing. For instance, how would you test that the input is an 
iterable?

> ML style pattern matching is syntactic sugar, that combines "if" statements
> with tuple unpacking, access to object attributes, and assignments. It is a
> compact, yet very readable syntax for algorithms, that would otherwise require
> nested "if" statements.

Your example does not use nesting.

 > It is especially useful for writing interpreters, and
> processing complex trees.

Sounds like better suited to a special-purpose 3rd party module, like 
pyparsing.

> Instead of a specification in BNF, here is a function written with the
> proposed pattern matching syntax. It demonstrates the features that I find
> most important. The comments and the print statements explain what is done.
>
>
> Proposed Syntax
> ---------------
>
> def foo(x):
>      match x with
>      | 1 ->                       # Equality
>          print("x is equal to 1")
>      | a:int ->                   # Type check
>          print("x has type int: %s" % a)
>      | (a, b) ->                  # Tuple unpacking
>          print("x is a tuple with length 2: (%s, %s)" % (a, b))
>      | {| a, b |} ->              # Attribute existence and access
>          print("x is an object with attributes 'a' and 'b'.")
>          print("a=%s, b=%s" % (a, b))
>
>      # Additional condition
>      | (a, b, c) with a>  b ->
>          print("x is a tuple with length 3: (%s, %s, %s)" % (a, b, c))
>          print("The first element is greater than the second element.")
>
>      # Complex case
>      | {| c:int, d=1 |}:Foo ->
>          print("x has type Foo")
>          print("x is an object with attributes 'c' and 'd'.")
>          print("'c' has type 'int', 'd' is equal to 1.")
>          print("c=%s, d=%s" % (c, d))
>
>      # Default case
>      | _ ->
>          print("x can be anything")
>
>
> Equivalent Current Python
> -------------------------
>
> The first four cases could be handled more simply, but handling all cases in
> the same way leads IMHO to more simple code overall.
>
> def foo(x):
>      while True:
>          # Equality
>          if x == 1:
>              print("x is equal to 1")
>              break
>
>          # Type check
>          if isinstance(x, int):
>              a = x
>              print("x is an integer: %s" % a)
>              break
>
>          # Tuple unpacking
>          if isinstance(x, tuple) and len(x) == 2:
>              a, b = x
>              print("x is a tuple with length 2: (%s, %s)" % (a, b))
>              break
>
>          # Attribute existence testing and access
>          if hasattr(x, "a") and hasattr(x, "b"):
>              a, b = x.a, x.b
>              print("x is an object with attributes 'a' and 'b'.")
>              print("a=%s, b=%s" % (a, b))
>              break
>
>          # Additional condition
>          if isinstance(x, tuple) and len(x) == 3:
>              a, b, c = x
>              if a>  b :
>                  print("x is a tuple with length 3: (%s, %s, %s)" % (a, b, c))
>                  print("The first element is greater than the second "
>                          "element.")
>                  break
>
>          # Complex case
>          if isinstance(x, Foo) and hasattr(x, "c") and hasattr(x, "d"):
>              c, d = x.c, x.d
>              if isinstance(c, int) and d == 1:
>                  print("x has type Foo")
>                  print("x is an object with attributes 'c' and 'd'.")
>                  print("'c' has type 'int', 'd' is equal to 1.")
>                  print("c=%s, d=%s" % (c, d))
>                  break
>
>          # Default case
>          print("x can be anything")
>          break
>
>
> Additional Code to Run Function "foo"
> -------------------------------------
>
> class Bar(object):
>      def __init__(self, a, b):
>          self.a = a
>          self.b = b
>
> class Foo(object):
>      def __init__(self, c, d):
>          self.c = c
>          self.d = d
>
>
> foo(1)          # Equality
> foo(2)          # Type check
> foo((1, 2))     # Tuple unpacking
> foo(Bar(1, 2))  # Attribute existence testing and access
> foo((2, 1, 3))  # Additional condition
> foo(Foo(2, 1))  # Complex case
> foo("hello")    # Default case
>
>
>
> I left out dict and set, because I'm not sure how they should be handled. I
> think list should be handled like tuples. Probably there should be a universal
> matching syntax for all sequences, similarly to the already existing syntax:
>      a, b, *c = s
>
> I don't really like the "->" digraph at the end of each match case. A colon
> would be much more consistent, but I use colons already for type checking
> (a:int).
>
> I generally think that Python should acquire more features from functional
> languages. In analogy to "RPython" it should ultimately lead to "MLPython", a
> subset of the Python language that can be type checked and reasoned about by
> external tools, similarly to what is possible with Ocaml.
>
>
>
> Eike.


-- 
Terry Jan Reedy




More information about the Python-ideas mailing list