3
votes

I have a list of binary functions such that (a -> b -> a) and a list of items [b]. I need to produce a transformation (a -> a) as follows. For each of the bs I need to partially apply the functions and produce their composition, which gives me a list of unary functions the same length as the bs. Call each of these compositions a processor; I next need to compose the processors. So for example, if I have functions [f0,f1] and items [b0,b1], I need the function (f1 b1) . (f0 b1) . (f1 b0) . (f0 b0). I can see that I'm folding across the bs, but this also looks like a composition of an outer product. So, does Haskell offer a clever way to produce this with some kind of outer product? (Order matters, of course.) If so, please explain in terms understandable to a complete novice.

2
I say to have a look at <*> in Control.Applicative.ThreeFx
How do you want to determine the order of the composition? You give an example for lists of length 2, but what should it be for 3, 4...?Free_D

2 Answers

3
votes

You can use <**> (which is located in Control.Applicative) and fold the result:

combine :: [(a -> b -> a)] -> [b] -> (a -> a)
combine fs bs = foldr (flip (.)) id $ bs <**> (map flip fs)

Let's have a look at the components:

map flip fs

This just flips the functions from a -> b -> a to b -> a -> a so we can ap them later on.

bs <**> (map flip fs)
-- ex: [b0, b1] <**> [f0, f1] == [f0 b0, f1 b0, f0 b1, f1 b1]

This function partially applys all the bs to the fs and puts them in a list. All we have to do now is to fold the result using the composition operator ((.)). Note that using flip (.) we reverse order of composition since the list generated by <**> is the reverse of what we want:

foldr (flip (.)) id $ it -- alternatively foldr1
-- ex: foldr (flip (.)) id [f0 b0, f1 b0, f0 b1, f1 b1] ==
--     (f1 b1) . (f0 b1) . (f1 b0) . (f0 b0)
3
votes

See ThreeFx's answer for a solution using only standard functions.


You also need to fold the list of partially applied functions itself with (.).

First, get your list of partially applied functions:

-- If f :: a -> b -> a, then (`f` b) :: a -> a
[ (`f` b) | b <- [b0, b1], f <- [f0, f1] ]
-- Result: [(`f0` b0), (`f1` b0), (`f0` b1), (`f1` b1)]

(It would be nice to use fs <*> bs but you need to partially apply f to its second argument, not first, and the resulting list would have the wrong order for the composition step.)

Next, fold that list with (.). I can never quite remember which fold to use (or how to use it to ensure the correct ordering you need), so here is a custom recursive function that I know does the right thing:

composeAll :: [(a -> a)]
composeAll [] = id
composeAll (f:fs) = composeAll fs . f

Putting it all together:

fs = [f0, f1, f2]
bs = [b0, b1, b2]
f = composeAll [(`f` b) | b <- bs, f <- fs]