This is the sort of code that writes itself - you just have to make the types join up. But there are a couple of standard tools (namely Applicative
and Traversable
) which you can use to make your code shorter.
In the Data.Traversable
module lives sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
. When specialised to []
's instance of Traversable
and the function applicative (->) r
we get:
sequenceA :: [r -> a] -> r -> [a]
So sequenceA
yanks ->
out of []
, applying each function in the list to a fixed argument.
sequenceA [(< 7), (> 7), (== 7)] :: (Num n, Ord n) => n -> [Bool]
-- equivalent to:
\n -> map ($ n) [(< 7), (> 7), (== 7)]
So your first function can be written as
f :: (Num n, Ord n) => n -> Bool
f = and . sequenceA [(< 7), (> 7), (== 7)]
I'm using and
instead of mconcat . map (All .)
.
For your second function, uncurry
is the right tool. We have to map uncurry
over the list of binary functions to get a list of unary functions of tuples, so that we can hoist the single argument out using sequenceA
. Because traverse f = sequenceA . map f
we can write it as:
g :: Ord n => n -> n -> Bool
g = curry $ and . traverse uncurry [(<), (>), (==)]
(NB, for any correctly-implemented instance of Ord
, >
and <
should be mutually exclusive. So both of these functions will always return False
.)