1

I have a list of functions of type a -> b -> Bool, and am trying to apply them to two inputs and combine the results with All or Any. I have this working with functions of one variable:

mconcat (map (All . ) [(<7),(>7),(==7)]) $ 6

but I cannot figure out how to do the same with two variable functions.

This works:

mconcat (map (All . ) (map (uncurry) [(<),(>),(==)])) $ (,) 6 7

but it looks to me like an ugly workaround.

Is there a better way to do this?

Jan Hlavacek
  • 207
  • 2
  • 5

2 Answers2

3

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.)

Benjamin Hodgson
  • 42,952
  • 15
  • 108
  • 157
1

A close alternative to the original code:

mconcat (map (\f a b -> All (f a b)) [(<),(<=)]) 3 4

One can further rewrite \f a b -> All (f a b) in pointless style:

mconcat (map ((.) (All .)) [(<),(<=)]) 3 4
chi
  • 111,837
  • 3
  • 133
  • 218