dopatraman
dopatraman

Reputation: 13908

How to apply partial functions to list of tuples

The title of this question might sound misleading, but I wasnt sure how else to explain what I'm trying to do.

I'm experimenting with non-deterministic data structures. I'm trying to produce all possible combinations of a set of partially apply operators and a list of tuples:

-- test.hs
makeTupleList :: (Int, Int) -> [(Int, Int)] 
makeTupleList (a, b) = ZipList [(+2), (-2)] <*> ZipList [a, b]

I want makeTupleList to return something like:

[(a + 2, b + 2), (a + 2, b - 2), (a - 2, b + 2), (a - 2, b - 2)]

But I'm clearly doing something wrong because I keep getting this error:

Couldn't match type `ZipList' with `[]'

How do I get my desired result?

Upvotes: 1

Views: 255

Answers (2)

TallerGhostWalt
TallerGhostWalt

Reputation: 464

I would drop the ZipList:

makeTupleList :: (Int, Int) -> [(Int, Int )]
makeTupleList (a,b) = (,) <$> aList <*> bList
 where
  functionList :: [Int -> Int]
  functionList = [flip (-) 2  ,(+ 2) ]
  aList :: [Int]
  aList = functionList <*> [a]
  bList = functionList <*> [b]

Yielding:

 λ> makeTupleList (1,1)
 [(-1,-1),(-1,3),(3,-1),(3,3)]

ZipList is for not generating permutations out of an applicative action.

Upvotes: 4

sclv
sclv

Reputation: 38893

There's a mismatch between the type you declared and the type the function actually has.

Let's just check the body of the function

*> :t \(a, b) -> ZipList [(+2), (-2)] <*> ZipList [a, b]
\(a, b) -> ZipList [(+2), (-2)] <*> ZipList [a, b]
  :: (Num (b -> b), Num b) => (b, b) -> ZipList b

Eep! That's not good at all.

So let's fix the error. The first error is that -2 trips you up, because it parses as negative two, not an operator section.

You can see that by typechecking just that fragment:

*> :t (-2)
(-2) :: Num a => a

Ok, so we'll change it to an explicit lambda, and get closer:

*> :t \(a, b) -> ZipList [(+2), (\x -> x - 2)] <*> ZipList [a, b]
\(a, b) -> ZipList [(+2), (\x -> x - 2)] <*> ZipList [a, b]
  :: Num b => (b, b) -> ZipList b

Now we see the conceptual error (you can see even more if you test it). We're just getting back [a+2,b-2]. That's what ziplist does -- "zips" the lists together. It doesn't take the cross product.

So now we see we're on the completely wrong track.

When we want to take a "producty" combination, I find the most natural way is to use a list comprehension. So let's just do that instead.

makeTupleList (a,b) = let funs = [(+2),(\x->x-2)]
                      in [(f a, g b) | f <- funs, g <- funs]

And that seems to do it.

*> makeTupleList (10,20)
[(12,22),(12,18),(8,22),(8,18)]

Upvotes: 4

Related Questions