prosseek
prosseek

Reputation: 190799

Using list comprehension to make a list of new type in Haskell

I'm trying to make a function that returns a list of assoc type that I defined from two lists. I think about using list comprehension, but I'm not sure how to get a value from each list.

type Assoc k v = [(k, v)]

makeAssoc :: [k] -> [v] -> [Assoc k v]
makeAssoc k v =
   [Assoc k' v' | k' <- k, ???]

main :: IO ()
main = do
    putStrLn $ show makeAssoc [1,2,3] [4,5,6]

How to implement the makeAssoc k v?

Upvotes: 0

Views: 944

Answers (1)

sonologico
sonologico

Reputation: 764

You have defined Assoc k v as an alias (type does not create a distinct type, just gives an existing type a new name - they are still interchangeable) for a list of (k, v). According to the makeAssoc type signature, it returns a list of Assoc k v. Which means that it actually returns a list of list of (k, v) and that is probably not what you want. Two possible solutions:

Solution 1 - Keep Assoc k v as a list of tuples and remove the [] from the type signature of makeAssoc

type Assoc k v = [(k, v)]

makeAssoc :: [k] -> [v] -> Assoc k v

Solution 2 - Make Assoc k v an alias for a tuple (not a list of tuples) and keep the [] in the type signature of makeAssoc

type Assoc k v = (k, v)

makeAssoc :: [k] -> [v] -> [Assoc k v]

Another problem is thattype Assoc k v creates the alias Assoc k v that you can use on type signatures, but it doesn't create an actual distinct type with a corresponding data constructor. So you can't use Assoc k v on the actual implementation of the function. That means we are working with simple tuples and the correct syntax for list comprehensions would be (using the second solution from before):

type Assoc k v = (k, v)

makeAssoc :: [k] -> [v] -> [Assoc k v]
makeAssoc ks vs = [(k, v) | k <- ks, v <- vs]

But list comprehensions will give you every possible pair using one element from each list. Exampe: makeAssoc [1, 2] [3, 4] will result in [(1, 3), (1, 4), (2, 3), (2, 4)]. If you actually want a one to one direct mapping, you should use zip.

type Assoc k v = (k, v)

makeAssoc :: [k] -> [v] -> [Assoc k v]
makeAssoc ks vs = zip ks vs

With this implementation, the result would be [(1, 3), (2, 4)].

Edit:

Going further: when you get more experienced in Haskell, you will learn that you could have written the same thing as:

type Assoc k v = (k, v)

makeAssoc :: [k] -> [v] -> [Assoc k v]
makeAssoc = zip

Another thing: If you wanted an actual distinct type, you could have used the data keyword and the zipWith function.

data Assoc k v = Assoc k v
    deriving (Show)

makeAssoc :: [k] -> [v] -> [Assoc k v]
makeAssoc ks vs = zipWith Assoc ks vs

Or omitting the parameters as before:

data Assoc k v = Assoc k v
    deriving (Show)

makeAssoc :: [k] -> [v] -> [Assoc k v]
makeAssoc = zipWith Assoc

Learn you a Haskell is a great resource for a beginner and should help you understand all this and much more.

Upvotes: 5

Related Questions