Less is More
One of the beautiful things about Python is its simplicity. We don’t want it to end up like those languages which have a designed-by-committee feel where everyone gets to add features and there are many ways of doing everything. Every feature that is added not only has to have some value but that value must outweigh the cost of the additional complexity. The more features, the more learning is required before being able to understand and edit other people’s code; the greater the risk that learning will be broad and shallow; and the larger the bug surface for the language. Simple is good.
In the previous blog post I concluded that Pattern Matching was a positive addition to the language. After looking further into the gotchas of Python Pattern Matching, and listening to the arguments of a friend (you know who you are :-)), I have become much less sure. In balance, I suspect Python Pattern Matching probably doesn’t pass the Must-Be-Really-Valuable-To-Justify-the-Increased-Complexity test.
Arguments Against Python Pattern Matching
This section includes some material from the previous blog post but with a different emphasis and more detail.
Similarity to object instantiation misleading
Imagine we have a Point class:
def __init__(self, x, y):
case Point(x, y): seems to me to be an obvious way of looking for such a
Point object and unpacking its values into
y but it isn’t allowed. It is a perfectly valid syntax for instantiating a
Point object but we are not instantiating an object and supplying the object the case condition – instead we are supplying a pattern to be matched and unpacked. We have to have a firm grasp on the notion that Python patterns are not objects. If we forget we get a TypeError:
case Point(0, 999):
TypeError: Point() accepts 0 positional sub-patterns (2 given)
Note, we must match the parameter names (the left side) but can unpack to any variable names we like (the right side). For example, all of the following will work:
case Point(x=x, y=y):
case Point(x=lon, y=lat):
case Point(x=apple, y=banana):
case Point(a=x, b=y):
It is a bit disconcerting being forced to use what feel like keyword arguments in our patterns when the original class definition is optionally positional. We should expect lots of mistakes here and it’ll require concentration to pick them up in code review.
Similarity to isinstance misleading
case float: don’t work as you might expect. The proper approach is to supply parentheses: using the example of integer patterns, we need
case int():, or, if we want to “capture” the value into, say,
case int(x):. But if we don’t know about the need for parentheses, or we slip up (easy to do) these inadvertant patterns will match anything and assign it to the name
str etc. Definitely NOT what we want.
The only protection against making this mistake is when you accidentally do this before other case conditions – e.g.
SyntaxError: name capture 'int' makes remaining patterns unreachable
Otherwise you’re on your own and completely free to make broken code. This will probably be a common error because of our experience with
isinstance where we supply the type e.g.
isinstance(x, int). Which reminds me of a passage in Through the Looking-Glass, and What Alice Found There by Lewis Carroll.
‘Crawling at your feet,’ said the Gnat (Alice drew her feet back in some alarm), ‘you may observe a Bread-and-Butterfly. Its wings are thin slices of Bread-and-butter, its body is a crust, and its head is a lump of sugar.’
‘And what does it live on?’
‘Weak tea with cream in it.’
A new difficulty came into Alice’s head. ‘Supposing it couldn’t find any?’ she suggested.
‘Then it would die, of course.’
‘But that must happen very often,’ Alice remarked thoughtfully.
‘It always happens,’ said the Gnat. After this, Alice was silent for a minute or two, pondering.
There will be lots of mistakes when using the match case even in the most common cases. And more complex usage is not readable without learning a lot more about Pattern Matching. See PEP 622.
Basically it is a yet another mini-language to learn.
Arguments for Python Pattern Matching
Sometimes we need to pattern match and a match / case syntax is quite elegant. The way values are unpacked into names is also really nice.
We will certainly love this feature if we are moving away from duck typing and adopting the programming style of statically-typed languages like Scala. But maybe we shouldn’t encourage this style of programming by making it easier to write in Python.
In balance, Python Pattern Matching doesn’t seem to pass the Must-Be-Really-Valuable-To-Justify-the-Increased-Complexity test. I enjoyed coming to grips with the syntax but I think it is like
for else and Python Enums – best avoided. But we will see. We can’t always tell what the future of a feature will be – maybe it will turn out to be very useful and one day there will be a third blog post ;-).