Index ¦ Archives ¦ Atom

Adding functional style pattern matching to Python

One of the nicest features of functional programming languages like Haskell and Erlang is pattern matching. An example, from Learn You Some Erlang:

greet(male, Name) ->
    io:format("Hello, Mr. ~s!", [Name]);
greet(female, Name) ->
    io:format("Hello, Mrs. ~s!", [Name]);
greet(_, Name) ->
    io:format("Hello, ~s!", [Name]).

The function body that is called is the one where the pattern matches what is passed in. It’s sort of like overloading functions in C++, but instead of just based on types, it’s much more generic.

I program in Python, and I’ve thought for a while that I could probably implement this in Python. I had a few breakthroughs about how to do it last night, and so inspired in part by other language syntax hacks, like the goto statement and anonymous/ruby blocks I put together a pretty neat prototype and uploaded it to a github gist: https://gist.github.com/1320421.

Here are some examples of what you can do:

from patternmatching import ifmatches, Any, OfType, Where

@ifmatches
def greet(gender=OfType(str), name="Joey"):
    print "Joey, whats up man?"
@ifmatches
def greet(gender="male", name=Any):
    print "Hello Mr. %s" % name
@ifmatches
def greet(gender="female", name=Any):
    print "Hello Ms. %s" % name
@ifmatches
def greet(gender=Any, name=Any):
    print "Hello, %s" % name

You can match parameters on four different ways

  • by its type with
def fun(a=OfType(str, int)):
  • by an exact match
def fun(a=54):
  • with any arbitrary predicate
def fun(a=Where(str.isupper)):
  • with a “wildcard”
def fun(a=Any):

Known Limitations

  • Cannot be called with keyword arguments, due to limitations of python’s function introspection. I might be wrong about this though. I’m sure it’s possible somehow, but I’d rather not do anything like say… tokenize the file myself.
  • Does not have data deconstruction like Haskell and Erlang, but I think I know how to do this after consulting with colleague
  • You give up the ability to use *args/**kwargs with your function, since the ideas are not especially compatible
  • You must specify every argument as a named parameter in the definition. Each clause/def does not need to have consistent naming, at least not yet. In fact, they don’t even need to have the same number of arguments.

Similar Implementations

  • Guido did something similar with "multimethods"; the implementation is somewhat similar but it's more like C++; you can only overload based on types, not on arbitrary "matching."
  • Ian Bicking wrote patmatch which is roughly the same idea (I found it afterwards) but I don't like it as much; the implementation is longer and it doesn't let you reuse the method's names (each possible match is a different function name and is associated to the original with a decorator). Also it doesn't abuse default parameters but instead uses decorator arguments.

© Chad Selph. Built using Pelican. Theme by Giulio Fidente on github.