countif function is struggling to count anything because you told it to make a list:
count_if f  = 0 -- fine, makes sense
count_if f (x:xs)
| f x = x : count_if f xs -- Oops, no!
| otherwise = count_if f xs -- yup
: is for putting an extra element on the front of a list. If you want to count, you want a number, not a list. (Putting
x on the front sounds a lot like
filter, which gives you all the elements where your predicate is true, but you wanted to count, which is different.)
You'll spot this type of error more easily if you tell the compiler what you were expecting by giving an explicit type signature like
count_if :: (a -> Bool) -> [a] -> Int. Instead of putting
x on the front with
x:, let's add one with
count_if :: (a -> Bool) -> [a] -> Int
count_if f  = 0
count_if f (x:xs)
| f x = 1 + count_if f xs -- adds one to the total from the rest
| otherwise = count_if f xs
Now that can be tested like this:
> count_if (>5) [1..10]
> count_if (=='e') "Time is of the essence"
> count_if even [1..100]
Now you can make
filter. The type of filter is
filter :: (a -> Bool) -> [a] -> [a] and it gives just the elements that you need:
> filter (>5) [1..10]
> filter (=='e') "Time is of the essence"
but then do length on the result:
countif' :: (a -> Bool) -> [a] -> Int
countif' f xs = length (filter f xs)
But that can be written slightly neater as
countif :: (a -> Bool) -> [a] -> Int
countif f = length . filter f
. is function composition - this says filter with
f, then take the length.
(Pointfree geeks would prefer to write this as
countif = (length.).filter but that's a lesson for another day!)
Using standard functions like
length can result in performance enhancements you might not spot by yourself. If you test
countif (>0) [1..1000000] against
count_if (>0) [1..1000000], you'll find it runs noticably faster. It's a good idea to get to know prelude functions like
scanr etc from the prelude because of this.