32

How can I best convert a list to a tuple in Haskell:

[1,2,3,4,5,6] -> (1,2,3,4,5,6)
P Shved
  • 96,026
  • 17
  • 121
  • 165
Alex
  • 1,581
  • 2
  • 18
  • 31

8 Answers8

53

In a general way, you can't. Each size of tuple is a distinct type, whereas lists of any length are a single type. Thus, there's no good way to write a function that takes a list and returns a tuple of the same length--it wouldn't have a well-defined return type.

For instance, you could have functions like:

tuplify2 :: [a] -> (a,a)
tuplify2 [x,y] = (x,y)

tuplify3 :: [a] -> (a,a,a)
tuplify3 [x,y,z] = (x,y,z)

...but not one that does the job of both.

You can write a generic version using various kinds of meta-programming, but you'd rarely want to.

Note that the same problem applies to other things, such as writing class instances for various tuples--take a look at the source code for Data.Tuple from the standard libraries!

C. A. McCann
  • 76,893
  • 19
  • 209
  • 302
  • How can that be the real source of `Data.Tuple`? I mean... the second element in the export list is: `, snd -- :: (a,b) -> a`. Is this glaring error in the types really there? – Bakuriu Jul 20 '14 at 14:16
  • 2
    @Bakuriu: Ha, that's an amusing typo. It's only a comment, of course--the type signature on `snd` near the end of the file is correct. That's also a fairly old version of `Data.Tuple`, more recent versions don't have that error. – C. A. McCann Jul 24 '14 at 15:14
27

Template Haskell is as close as you can get due to type checking if you want to extract a variable number of elements, since (a,b) and (a,b,c) have different types.

{-# LANGUAGE TemplateHaskell #-}
import Language.Haskell.TH
tuple :: Int -> ExpQ
tuple n = do
    ns <- replicateM n (newName "x")
    lamE [foldr (\x y -> conP '(:) [varP x,y]) wildP ns] (tupE $ map varE ns)

Then:

$(tuple 6) [1,2,3,4,5,6] == (1,2,3,4,5,6)
$(tuple 3) "abc" == ('a','b','c')

But in general, if you need this answer, then you're asking the wrong question somewhere.

If you just want flat random access, perhaps the better option would be to use an Array.

Edward Kmett
  • 29,632
  • 7
  • 85
  • 107
  • 4
    Template Haskell is awesome, but I wish it was just a little bit more readable. Then again, maybe it's best that it isn't -- otherwise people would use it more than they really should. – Benjamin Kovach Jun 10 '14 at 13:25
  • Apparently `Control.Lens` gets even closer with `map (^..each)`, which also works for arbitrary tuples. — Not that anything that is “for arbitrary tuples” has anything to do with the whole point of tuples. (That’s what lists are for. Tuples are used precisely when one wants to prevent this kind of usage.) – Evi1M4chine Jul 29 '15 at 20:12
  • Due to the way `each` uses lazy patterns: `ghci> undefined & partsOf each .~ [1,2,3,4] :: (Int,Int,Int,Int)` yields (1,2,3,4). So you can go both ways up to size 9 or so. Of course you need to know the result tuple type. – Edward Kmett Aug 28 '15 at 01:34
16

If feel like I am about to advise you to point a gun at your foot and trust you not to shoot.

> list2Tuple lst = read $ "(" ++ (init.tail.show) lst ++ ")"
> list2Tuple [1,2,3] :: (Int, Int, Int)
(1,2,3)
> list2Tuple [1,2,3,4] :: (Int, Int, Int, Int)
(1,2,3,4)

This will work up to the what ever length of tuple Show and Read are defined for.

Davorak
  • 7,362
  • 1
  • 38
  • 48
8

Tuples and lists are very different things. About the best you can do is to manually write a conversion function:

toTuple :: [a] -> (a,a,a,a,a,a)
toTuple [a,b,c,d,e,f] = (a,b,c,d,e,f)

Notice how different the types are: the single variable of the list expands to six variables for the tuple. So you'll need one function for each size of tuple.

Nathan Shively-Sanders
  • 18,329
  • 4
  • 46
  • 56
  • 1
    You and camccann beat me. That's it, the best he can do is N functions and then a handler for calling one of these for a given N. – Spidey May 27 '10 at 13:14
  • tuples are lists in math. they are very similar. In Haskell's implementation, tuples have a fixed typed length and lists do not, so it is not easy to convert the two, but that is a language specific implementation issue. – clay Dec 18 '15 at 19:00
5

I find it difficult to make cogent explanations of Template Haskell manipulations, but here is a demonstration:

> :m +Language.Haskell.TH
> :set -XTemplateHaskell
> runQ [| [1,2,3,4,5,6] |] >>= putStrLn . pprint
[1, 2, 3, 4, 5, 6]
> runQ [| [1,2,3,4,5,6] |] >>= \ (ListE exps) -> putStrLn (pprint (TupE exps))
(1, 2, 3, 4, 5, 6)
David Fox
  • 51
  • 1
  • 1
3

I don't think it's possible to do this in Haskell, for a list of arbitrary length not known at compile time. Template Haskell can't do it because it operates only at compile time. I ran into a case where I needed to do precisely this, and I had to work around it. A database library expects different-length tuples for query arguments, but I have an arbitrary-length list. So I have to skirt around the library interface. It would be nice if it could take a list.

Basically the issue is that different-length tuples are different types. But the Haskell compiler has to know at compile type what types might exist at runtime. Converting an arbitrary-length list into a tuple at run time could create some type it didn't know about at compile time.

Lyle Kopnicky
  • 131
  • 1
  • 2
2

You can in fact do better than manually writing one function per tuple-size if you use quasi-quotation as explained here. However, I would wonder about code where you expect to use this generically.

geisterfurz007
  • 5,292
  • 5
  • 33
  • 54
StevenC
  • 1,172
  • 1
  • 9
  • 17
  • Hoping to work up a little example later today when I have time and edit my comment with it... – StevenC May 27 '10 at 17:01
  • 2
    Quasiquotation being part of Template Haskell, I believe, which is the meta-programming I referred to. In the end this is still producing one function per tuple size, it's just writing compile-time code to do it for you instead of writing them by hand. I've yet to get the hang of TH myself, so I look forward to seeing your example! – C. A. McCann May 27 '10 at 17:16
  • Edward Kmett beat me to it. I thought I'd done it with quasiquotes before, but I guess it was just TH. Sorry. – StevenC May 28 '10 at 01:17
0

When dealing with command line arguments you can use getArgs, that function will give you a list with strings:

getArgs :: IO [String] 

Link: https://hackage.haskell.org/package/base-4.16.0.0/docs/System-Environment.html#v:getArgs

When I work with command line arguments I prefer to work with tuples instead of a list, so I convert the list to a tuple. See below:

import System.Environment

main = do
    args <- getArgs
    let (a:b:c) = args
    print a

Calling the program (in PowerShell):

PS C:\Haskell> runghc convertListToTuple goodday to you
"goodday"
Julian
  • 4,396
  • 5
  • 39
  • 51