Consider the description of filter
in the docs:
filter
, applied to a predicate and a list, returns the list of those elements that satisfy the predicate; i.e.,
filter p xs = [x | x <- xs, p x]
To explain it to someone who doesn't understand list comprehensions, you might say filter
has three cases:
- (the easy case) when the list to be filtered is empty, the result is also empty
- when the head of the list to be filtered satisfies the predicate, it's part of the result
- otherwise, skip the head and filter the rest of the list
These cases correspond one-to-one with the last three lines of the definition in your question.
Small touches can make the definition more idiomatic and therefore easier to read:
filter _ [] = []
filter p (x:xs)
| p x = x : filter p xs
| otherwise = filter p xs
For an empty list, the predicate can be anything at all, and the underscore shows explicitly that it's unimportant in that case.
Rather than matching against (x:y)
, using (x:xs)
—think: "ex and exes"—emphasizes that you're separating a list into its head (of type a
) and tail (of type [a]
, i.e., list of a
).
Finally, lining up the recursive calls to filter
easily allows the reader to see that the latter case omits x
.