x in the generator expression is not just variable, but can be any pattern. In cases of pattern mismatch the generated element is skipped over, and processing of the list continues with the next element, thus acting like a filter:
[x | Just x <- [Just 1, Nothing, Just 3]] -- [1, 3]
A generator with a variable
x in its pattern creates new scope containing all the expressions on its right, where
x is defined to be the generated element.
This means that guards can be coded as
[ x | x <- [1..4], even x] == [ x | x <- [1..4], () <- [() | even x]] == [ x | x <- [1..4], () <- if even x then [()] else ]