The filter class of functions takes a condition (a -> Bool) and applies it when filtering.
What is the best way to use a filter on when you have multiple conditions?
The liftM2 combinator can be used in the Reader monad to do this in a 'more functional' way:
import Control.Monad
import Control.Monad.Reader
-- ....
filter (liftM2 (&&) odd (> 100)) [1..200]
Note that the imports are important; Control.Monad.Reader provides the Monad (e ->) instance that makes this all work.
The reason this works is the reader monad is just (e ->) for some environment e. Thus, a boolean predicate is a 0-ary monadic function returning bool in an environment corresponding to its argument. We can then use liftM2 to distribute the environment over two such predicates.
Or, in simpler terms, liftM2 will act a bit like this when the types work out:
liftM2 f g h a = f (g a) (h a)
You can also define a new combinator if you want to be able to chain these easily, and/or don't want to mess with liftM2:
(.&&.) :: (a -> Bool) -> (a -> Bool) -> (a -> Bool)
(.&&.) f g a = (f a) && (g a)
-- or, in points-free style:
(.&&.) = liftM2 (&&)
filter (odd .&&. (> 5) .&&. (< 20)) [1..100]