Convert a tuple of lists of equal length to a list of tuples for arbitrary - Haskell

I am trying to convert a tuple of lists of the same length: ([Int], [Int], ..)

to a list of tuples [(Int, Int, ...)]

. This can be done for given dimensions with the following code:

buildList :: ([a], [a], [a], [a], [a], [a], [a], [a]) -> [(a, a, a, a, a, a, a, a)]           
buildList ([], [], [], [], [], [], [], []) = []                                               
buildList (a:as, b:bs, c:cs, d:ds, e:es, f:fs, g:gs, h:hs) = (a, b, c, d, e, f, g, h) : buildList (as, bs, cs, ds, es, fs, gs, hs)

      

As you can probably see, it won't be very good when I need a lot of items in the list, and it would be much cleaner if it worked for any arbitrary value.

So my questions are: Do you have a function that teaches this operation for arbitrary length tuples?

+1


source to share


1 answer


As usual with questions like this, the answer to "should you" and "can" be different. Here I will answer strictly the second.

The first part of the solution to this question is representing n-ary tuples or (for the sound of fancy) products. Let's do this (without using imports to keep things self-contained, and also because the Stack is wrong on the machine I'm currently on):

{-# language DataKinds, KindSignatures, TypeOperators, GADTs #-}
{-# language FlexibleInstances, FlexibleContexts #-}
{-# language TypeFamilies #-} -- This will be needed later

data Product (ts :: [*]) where
    Nil :: Product '[]
    Cons :: t -> Product ts -> Product (t ': ts)

instance Show (Product '[]) where
    show Nil = "()"

instance (Show t, Show (Product ts)) => Show (Product (t ': ts)) where
    show (Cons x xs) = let '(':s = show xs
                       in concat ["(", show x, ",", s]

      

So this gives us a way to write something like

*Main> myPair = Cons 1 $ Cons "Foo" $ Cons True Nil 
*Main> :t myPair
myPair :: Num t => Product '[t, [Char], Bool]
*Main> myLists = Cons [1, 2] $ Cons ["Foo", "Bar", "Baz"] Nil
*Main> :t myLists
myLists :: Num t => Product '[[t], [[Char]]]

      

Using this type, at least we can start thinking about what type of our n-ary locking function zipN

should be:



zipN :: Product '[[a], [b], ...] -> [Product '[a, b, ...]]

      

however, we still need a way to somehow convert this tuple of lists Product '[[a], [b], ...]

to a tuple of elements Product '[a, b, ...]

. What I found easiest to do was use a linked type family to convert and actually lock in a lock:

class IsLists (ts :: [*]) where
    type Unlists ts :: [*]
    zipN :: Product ts -> [Product (Unlists ts)]

instance IsLists '[] where
    type Unlists '[] = '[]
    zipN Nil = []

instance (IsLists ts) => IsLists ([t] ': ts) where
    type Unlists ([t] ': ts) = t ': Unlists ts

    -- Handling the tail is special to ensure we don't end up zipping everything
    -- with the empty list coming from zipN []
    zipN (Cons xs yss) = case yss of
        Nil -> map (`Cons` Nil) xs
        _ -> zipWith Cons xs (zipN yss)

      

Example:

*Main> zipN myLists
[(1,"Foo",),(2,"Bar",)]
*Main> :t it
it :: Num t => [Product '[t, [Char]]]

      

Note that this behaves as usual zip

in that the length of the result list is specified in the shortest list in the tuple.

+3


source







All Articles