Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Manipulating Tuples in Haskell

Tags:

haskell

tuples

I'm new to Haskell, I have a question regarding tuples. Is there not a way to traverse a tuple? I understand that traversal is very easy with lists but if the input is given as a tuple is there not a way to check the entire tuple as you do with a list? If that's not the case would it possible to just extract the values from the tuple into a list and perform traversal that way?

like image 322
Karna Avatar asked Oct 13 '20 13:10

Karna


2 Answers

In Haskell, it’s not considered idiomatic (nor is it really possible) to use the tuple as a general-purpose traversable container. Any tuple you deal with is going to have a fixed number of elements, with the types of these elements also being fixed. (This is quite different from how tuples are idiomatically used in, for example, Python.) You ask about a situation where “the input is given as a tuple” but if the input is going to have a flexible number of elements then it definitely won’t be given as a tuple—a list is a much more likely choice.

This makes tuples seem less flexible than in some other languages. The upside is that you can examine them using pattern matching. For example, if you want to evaluate some predicate for each element of a tuple and return True if the predicate passes for all of them, you would write something like

all2 :: (a -> Bool) -> (a, a) -> Bool
all2 predicate (x, y) = predicate x && predicate y

Or, for three-element tuples,

all3 :: (a -> Bool) -> (a, a, a) -> Bool
all3 predicate (x, y, z) = predicate x && predicate y && predicate z

You might be thinking, “Wait, you need a separate function for each tuple size?!” Yes, you do, and you can start to see why there’s not a lot of overlap between the use cases for tuples and the use cases for lists. The advantages of tuples are exactly that they are kind of inflexible: you always know how many values they contain, and what type those values have. The former is not really true for lists.

Is there not a way to traverse a tuple?

As far as I know, there’s no built-in way to do this. It would be easy enough to write down instructions for traversing a 2-tuple, traversing a 3-tuple, and so on, but this would have the big limitation that you’d only be able to deal with tuples whose elements all have the same type.

Think about the map function as a simple example. You can apply map to a list of type [a] as long as you have a function a -> b. In this case map looks at each a value in turn, passes it to the function, and assembles the list of resulting b values. But with a tuple, you might have three elements whose values are all different types. Your function for converting as to bs isn’t sufficient if the tuple consists of two a values and a c! If you try to start writing down the Foldable instance or the Traversable instance even just for two-element tuples, you quickly realize that those typeclasses aren’t designed to handle containers whose values might have different types.

Would it be possible to just extract the values from the tuple into a list?

Yes, but you would need a separate function for each possible size of the input tuple. For example,

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

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

The good news, of course, is that you’re never going to get a situation where you have to deal with tuples of arbitrary size, because that isn’t a thing that can happen in Haskell. Think about the type signature of a function that accepted a tuple of any size: how could you write that?

In any situation where you’re accepting a tuple as input, it’s probably not necessary to convert the tuple to a list first, because the pattern-matching syntax means that you can just address each element of the tuple individually—and you always know exactly how many such elements there are going to be.

like image 54
bdesham Avatar answered Oct 14 '22 13:10

bdesham


If your tuple is a homogeneous tuple, and you don't mind to use the third-party package, then lens provides some functions to traverse each elements in an arbitrary tuple.

ghci> :m +Control.Lens
ghci> over each (*10) (1, 2, 3, 4, 5)   --traverse each element
(10,20,30,40,50)

Control.Lens.Tuple provides some lens to get and set the nth element up to 19th.

You can explore the lens package for more information. If you want to learn the lens package, Optics by examples by Chris Penner is a good book.

like image 30
Z-Y.L Avatar answered Oct 14 '22 13:10

Z-Y.L