hskjskj
hskjskj

Reputation: 121

Cartesian product of two lists of different types (Haskell)

I'm (very) new to Haskell and I'm trying to solve what I think is a simple problem:

I want to create a list of the Cartesian product of two lists of different types: eg. one contains characters and one contains integers.

   ['A','B','C']

and the infinite list:

    [1..]

I'm trying to get an output where each of my letters is suffixed with a number: eg.

    ["A1", "B1", "C1", "A2", "B2"..]

I've been attempting this using things I've read online, however I'm struggling. As I understand it, I want to convert my integer list to a string using 'show'. My code is below:

    combinations xs cs = (,) <$> xs (show <$> cs)

Where xs and cs are passed in my function as ['A','B','C'] and [1..] respectively.

However I receive this error:

   Couldn't match expected type ‘[String] -> [a0]’
              with actual type ‘[Char]’

I'd really appreciate any ideas as I've been struggling for a while.

Thanks

Upvotes: 0

Views: 185

Answers (1)

amalloy
amalloy

Reputation: 91837

(,) <$> xs (show <$> cs)

parses as

(,) <$> (xs (show <$> cs))

Note that here you are calling xs as a function, with show <$> cs as its argument. That can't be right: xs is a String!

Since you seem to be trying to program in applicative style, you probably meant to write something of the form

f <$> a <*> b

In your case,

f = (,)
a = xs
b = show <$> cs

So, you meant to write:

(,) <$> xs <*> (show <$> cs)

This is sorta close to what you wanted, but there are a few problems. First, its type is [(Char, String)], where you wanted them joined up as strings. That can be fixed by using (:) instead of (,).

Second, since one list is infinite, you need to process that as your "outermost loop" - if it's an inner loop, the fact that it never ends will mean your other loops never progress. When using [] as an Applicative, the "outer loop" is the first thing you map over.

So, we might try:

(,) <$> (show <$> cs) <*> xs

Note I've gone back to (,) for a moment to highlight something:

Prelude> take 5 $ (,) <$> (show <$> cs) <*> xs
[("1",'A'),("1",'B'),("1",'C'),("2",'A'),("2",'B')]

Now we get the pairs you want, but (:) won't work on them anymore because they're in the wrong order. So, you need to use a different function, one like (:) but which takes its arguments in the other order. Happily, it is easy to produce such a function: it's simply flip (:):

Prelude> take 5 $ (flip (:)) <$> (show <$> cs) <*> xs
["A1","B1","C1","A2","B2"]

Upvotes: 2

Related Questions