Well, you can use it to match on any value. So it's more useful than just slicing up lists.
Python has a primitive form of pattern matching in the form of unpacking, for example, this is legal code:
def tuple_decomp( (a,b) ):
return a+b
You pass in a 2-tuple (1,2) and 1 gets put into `a` and 2 gets put into `b`. It will error on anything but a 2-tuple.
As I showed in my examples, which I think you might have overlooked if you only saw the list slicing example, it provides a nice way of writing base cases for recursive functions:
factorial 0 = 1
factorial n = n * factorial (n-1)
as opposed to
factorial n = if n == 0
then 1
else n * factorial (n-1)
It's also really useful if you have a special case that you can provide a better implementation for:
multiply 0 _ = 0
multiply _ 0 = 0
multiply n m = n * m
The _ means "ignore this argument, I won't use it". So in the case that either arg is 0, we just fast-fail and return 0. Since Haskell is lazy, if I say
multiply (f (g (h 999))) 0
where f(g(h 999))) is going to be some huge time consuming computation, the call will return instance because it will see the 0 and never even evaluate the first argument.
In C et al, the arguments to a function are evaluated before they are passed in, so if I say
multiply( f(g(h(999))), 0);
It will calculate the value of f(g(h(999))) and send it in. In Haskell, you just send in a sort of pointer which points to the unevaluated expression, which, if you ask for it, will be evaluated.
Using pattern matching for decomposition is particularly useful in Haskell because of the custom datatypes. In Haskell, you can sort-of think of a data type as a C-union of C-structs. Let's say I define a datatype for Pets.
data Pet =
Cat {
color :: String,
breed :: String,
annoying :: Bool
} |
Dog {
breed :: String,
trained :: Bool
}
So I can make a Cat or a Dog, but both are still of type Pet. They contain different parameters within them. So I can't ask a Cat if it's trained, and I can't ask a Dog if it's annoying.
Let's say I want to make a function to determine if I like a pet. My criteria are: I only like trained dogs. If a Dog is trained, I like it. As for cats, I like it if it is not annoying, or if it is Yellow.
Rather than a mess of if statements, it's much more elegant to use pattern matching!
So as you can see, I can pattern match on constructors (Cat or Dog), and on the values within the types without putting any code in the actual function body! Compare that code to the same code with if statements and tell me which is prettier!