[Python-ideas] ML Style Pattern Matching for Python

Carl M. Johnson cmjohnson.mailinglist at gmail.com
Sat Dec 18 02:25:54 CET 2010


It's already trivial to make a decorator to do pattern matching, and
if PJE ever finished generic functions, it will get trivial-er. You
just need to create a simple decorator to do something like this:

@basecase
def fac(n):
    return n * fac(n -1)

@f.equals(1):
def f(n):
    return 1

@f.lessthan(1)
def f(n):
    raise Error("n less than 1, operation not defined.")

It's not that pretty, but I don't see any need for a whole new syntax
for what is basically a glorified elif.

On Fri, Dec 17, 2010 at 2:14 PM, Terry Reedy <tjreedy at udel.edu> wrote:
> 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
>
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> http://mail.python.org/mailman/listinfo/python-ideas
>



More information about the Python-ideas mailing list