I'm new to Haskell and are playing around a bit. I created a recursive function with guards. See function below:
filterAge :: [Person] -> [String]
filterAge (x:xs)
| (x:xs) == [] = []
| (getAge x) < 30 || (getAge x) > 40 = [] ++ filterAge xs
| otherwise = [getName x] ++ filterAge xs
I have a data set created with 10 Persons which I use in this method. When I tried this function it gave all the right people, but after that it got a Non-exhaustive patterns error:
["Lise","Jaap","Elle","Ebba"*** Exception: D:\...:(44,1)-(47,77): Non-exhaustive patterns in function filterAge
I found out that it never reaches the first guard. So I played around a bit and found out something really strange (in my opinion):
*Main> let (x:xs) = []
*Main> (x:xs) == []
False
Now my main question is: Why does (x:xs) == []
return False?
If anyone has a better way for me of doing the function that'd be great, but it's not very important.
Thanks in advance!
EDIT
Thanks to Willem Van Onsem and Lambda.xy.x I got a quick answer to my question. This resulted in the following function which works perfectly:
filterAge :: [Person] -> [String]
filterAge [] = []
filterAge (x:xs)
| (getAge x) < 30 || (getAge x) > 40 = [] ++ filterAge xs
| otherwise = [getName x] ++ filterAge xs
But for the best version you'd have to check the answer of Willem Van Onsem.
A list is defined as:
data [] a = [] | a : [a]
So there are two constructors for a list: []
the empty list, and (x:xs)
a constructor with one element, and a tail that can store an arbitrary number (zero or more) remaining elements.
Therefore (x:xs)
is a list with at least one element: the x
. The xs
can be an empty list (since it has type [a]
), but x
has type a
so that is the "head" of the list. Your let
statement works with pattern matching, and since the empty list cannot match with (x:xs)
, it will always fail.
Another implication is that your first guard can never fire. In order to fix the issue, you should implement a separate case for the empty list. Like:
filterAge :: [Person] -> [String]
filterAge [] = [] -- empty list case
filterAge (x:xs) -- first guard dropped
| (getAge x) < 30 || (getAge x) > 40 = [] ++ filterAge xs
| otherwise = [getName x] ++ filterAge xs
Note that we dropped the first guard in the second clause, since we know that will always fail, and thus checking this will (potentially) only cost CPU cycles.
There are still some parts we can optimize:
getAge
twice, which is useless, we can use a where
clause to optimize this;[] ++ somelist
is simply somelist
: appending after an empty list results in that list; and[element] ++ somelist
is element : somelist
, since now we work with the list constructor directly.So our filterAge
can be rewritten into:
filterAge :: [Person] -> [String]
filterAge [] = [] -- empty list case
filterAge (x:xs) | age < 30 || age > 40 = filterAge xs
| otherwise = getName x : filterAge xs
where age = getAge x
Note that if you compile (or start the interpreter) with the -Wincomplete-patterns
flag (warnings for incomplete patterns), Haskell will automatically warn you that your function definitions are not complete, and that there are input patterns for which you have not defined a clause.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With