[Python-ideas] ML Style Pattern Matching for Python
Eike Welk
eike.welk at gmx.net
Sat Dec 18 00:21:35 CET 2010
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.
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. It is especially useful for writing interpreters, and
processing complex trees.
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.
More information about the Python-ideas
mailing list