Will Ness's answer is correct. I just wanted to offer some general advice for Haskell and some tips for improving your code.
First, I would always avoid using guards. The syntax is quite inconsistent with Haskell's usual fare, and guards aren't composable in the same way that other Haskell syntax is. If I were you, I'd stick to using let, if/then/else, and pattern matching.
Secondly, an if statement in Haskell is very often not the right answer. In many cases, it's better to avoid using if statements entirely (or at least as much as possible). For example, a more readable version of count would look like this:
count el list = go list 0 where
go [] output = output
go (x:xs) output = go xs (if x == el
then 1 + output
else output)
However, this code is still flawed because it is not properly strict in output. For example, consider the evaluation of the expression count 1 [1, 1, 1, 1], which proceeds as follows:
count 1 [1, 1, 1, 1]
go [1, 1, 1, 1] 0
go [1, 1, 1] (1 + 0)
go [1, 1] (1 + (1 + 0))
go [1] (1 + (1 + (1 + 0)))
go [] (1 + (1 + (1 + (1 + 0))))
(1 + (1 + (1 + (1 + 0))))
(1 + (1 + 2))
(1 + 3)
4
Notice the ballooning space usage of this evaluation. We need to force go to make sure output is evaluated before it makes a recursive call. We can do this using seq. The expression seq a b is evaluated as follows: first, a is partially evaluated. Then, seq a b evaluates to b. For the case of numbers, "partially evaluated" is the same as being totally evaluated.
So the code should in fact be
count el list = go list 0 where
go [] output = output
go (x:xs) output =
let new_output = if x == el
then 1 + output
else output
in seq new_output (go xs new_output)
Using this definition, we can again trace the execution:
go [1, 1, 1, 1] 0
go [1, 1, 1] 1
go [1, 1] 2
go [1] 3
go [] 4
4
which is a more efficient way to evaluate the expression. Without using library functions, this is basically as good as it gets for writing the count function.
But we're actually using a very common pattern - a pattern so common, there is a higher-order function named for it. We're using foldl' (which must be imported from Data.List using the statement import Data.List (foldl')). This function has the following definition:
foldl' :: (b -> a -> b) -> b -> [a] -> b
foldl' f = go where
go output [] = output
go output (x:xs) =
let new_output = f output x
in seq new_output (go new_output xs)
So we can further rewrite our count function as
count el list = foldl' f 0 list where
f output x = if x == el
then 1 + output
else output
This is good, but we can actually improve even further on this code by breaking up the count step into two parts.
count el list should be the number of times el occurs in list. We can break this computation up into two conceptual steps. First, construct the list list', which consists of all the elements in list which are equal to el. Then, compute the length of list'.
In code:
count el list = length (filter (el ==) list)
This is, in my view, the most readable version yet. And it is also just as efficient as the foldl' version of count because of laziness. Here, Haskell's length function takes care of finding the optimal way to do the counting part of count, while the filter (el ==) takes care of the part of the loop where we check whether to increment output. In general, if you're iterating over a list and have an if P x statement, you can very often replace this with a call to filter P.
We can rewrite this one more time in "point-free style" as
count el = length . filter (el ==)
which is most likely how the function would be written in a library. . refers to function composition. The meaning of this is as follows:
To apply the function count el to a list, we first filter the list to keep only the elements which el ==, and then take the length.
Incidentally, the filter function is exactly what we need to write moreThan compactly:
moreThan a list = filter occursOften list where
occursOften x = count x list >= a
Moral of the story: use higher-order functions whenever possible.
Whenever you solve a list problem in Haskell, the first tool you should reach for is functions defined in Data.List, especially map, foldl'/foldr, filter, and concatMap. Most list problems come down to map/fold/filter. These should be your go-to replacement for loops. If you're replacing a nested loop, you should use concatMap.