71

In Haskell, I can easily map a list:

map (\x -> 2*x) [1,2] 

gives me [2,4]. Is there any "mapTuple" function which would work like that?

mapTuple (\x -> 2*x) (1,2) 

with the result being (2,4).

2
  • 3
    BTW, no need to use lambdas to map a simple multiplication over a list: simply map (*2) [1,2,3] would do the trick. Commented Nov 2, 2015 at 6:32
  • 1
    Note that no matter how you implement this, it will only work on tuples with the type: (a, a) Commented Nov 20, 2016 at 18:19

13 Answers 13

91

Here's a rather short point-free solution:

import Control.Monad (join) import Control.Arrow ((***)) mapTuple = join (***) 
Sign up to request clarification or add additional context in comments.

2 Comments

What is the effect of join when used on functions?
@Riccardo - join takes a function of two arguments with the same type, a->a->b, and creates a new function with one argument a -> b, passing that argument to both positions of the original function. This is because the Monad instance for functions is identical to the Reader monad, giving join type (a -> a -> b) -> a -> b. I find it a little easier to work out when the arrows aren't written infix, e.g. join :: (a ->) ( (a ->) b) -> (a ->) b.
52

Searching at Hoogle gives no exact matches for (a -> b) -> (a, a) -> (b, b), which is the type you require, but it is pretty easy to do yourself:

mapTuple :: (a -> b) -> (a, a) -> (b, b) mapTuple f (a1, a2) = (f a1, f a2) 

Note, you will have to define a new function for 3-tuples, 4-tuples etc - although such a need might be a sign, that you are not using tuples like they were intended: In general, tuples hold values of different types, so wanting to apply a single function to all values is not very common.

4 Comments

Control.Arrow is in the standard libs, and Control.Bifunctor istn't too far away...
@Landei: Yes, but the OP asked about mapping a single function over a tuple.
@Landei: I removed the mention of the standard libraries, since I don't know much about which modules are in them, or are going to be soon, and just mentioned hoogle, which was where I searched.
You can always use the Applicative trick to feed an argument twice in a function: (bimap <$> id <*> id) (*2) (3,5)
38

You could use Bifunctor:

import Control.Monad (join) import Data.Bifunctor (bimap) join bimap (2*) (1,2) 

This works not only for pairs, but for a number of other types as well, e.g. for Either.

Bifunctor is in base as of version 4.8. Previously it was provided by the bifunctors package.

2 Comments

The join trick for the (->) monad instance is awesome. Thanks!
Nice! That beats what I found in another context, where I just wanted to modify the second value, i.e. importData.Graph.Inductive.Query.Monad and then (*2) >< (*2) is the function you're after. (I'm puzzled that this isn't in Data.Tuple, but perhaps that's just an accident of history.)
25

You can also use lens to map tuples:

import Control.Lens mapPair = over both 

Or you can map over tuples with upto 10 elements:

mapNtuple f = traverseOf each (return . f) 

1 Comment

over each seems to work too: (over each) (+1) (1,2,3,4,5,6,7,8,9) == (2,3,4,5,6,7,8,9,10)
24

You can use arrows from module Control.Arrow to compose functions that work on tuples.

Prelude Control.Arrow> let f = (*2) *** (*2) Prelude Control.Arrow> f (1,2) (2,4) Prelude Control.Arrow> let f' = (*2) *** (*3) Prelude Control.Arrow> f (2,2) (4,4) Prelude Control.Arrow> f' (2,2) (4,6) 

Your mapTuple then becomes

mapTuple f = f *** f 

If with your question you asked for a function that maps over tuples of arbitrary arity, then I'm afraid you can't because they would have different types (e.g. the tuple types (a,b) and (a,b,c) are totally different and unrelated).

1 Comment

Do I need to load a module to use them? In plain Prelude your code doesn't work.
16

Here is another way:

mapPair :: (a -> b) -> (a, a) -> (b, b) -- this is the inferred type mapPair f = uncurry ((,) `on` f) 

You need Data.Function imported for on function.

2 Comments

I think this is the simplest and most elegant solution. +1
This only works for a tuple-2. It solves OP's problem, but isn't applicable to tuples of arbitrary sizes.
13

To add another solution to this colourful set... You can also map over arbitrary n-tuples using Scrap-Your-Boilerplate generic programming. For example:

import Data.Data import Data.Generics.Aliases double :: Int -> Int double = (*2) tuple :: (Int, Int, Int, Int) tuple = gmapT (mkT double) (1,2,3,4) 

Note that the explicit type annotations are important, as SYB selects the fields by type. If one makes one tuple element type Float, for example, it wouldn't be doubled anymore.

2 Comments

I understand vaguely that a lot of the original SYB libraries have been replaced by GHC Generics with some support in base/compiler. (But maybe you and that wikipedia page use "SYB" ad a term for generics in general, and not just the specific original SYB papers... )
I might be wrong, but given that GHC's Data.Data documentation cites SYB, I'd wager that it's a direct descendant.
9

Yes, for tuples of 2 items, you can use first and second to map the contents of a tuple (Don't worry about the type signature; a b c can be read as b -> c in this situation). For larger tuples, you should consider using a data structure and lenses instead.

Comments

9

The extra package provides the both function in the Data.Tuple.Extra module. From the docs:

Apply a single function to both components of a pair. > both succ (1,2) == (2,3) both :: (a -> b) -> (a, a) -> (b, b) 

Comments

7

You can also use Applicatives which have additional benefit of giving you possibility to apply different functions for each tuple element:

import Control.Applicative mapTuple :: (a -> a') -> (b -> b') -> (a, b) -> (a', b') mapTuple f g = (,) <$> f . fst <*> g . snd 

Inline version:

(\f -> (,) <$> f . fst <*> f . snd) (*2) (3, 4) 

or with different map functions and without lambda:

(,) <$> (*2) . fst <*> (*7) . snd $ (3, 4) 

Other possibility would be to use Arrows:

import Control.Arrow (+2) . fst &&& (+2) . snd $ (2, 3) 

Comments

5

I just added a package tuples-homogenous-h98 to Hackage that solves this problem. It adds newtype wrappers for tuples and defines Functor, Applicative, Foldable and Traversable instances for them. Using the package you can do things like:

untuple2 . fmap (2 *) . Tuple2 $ (1, 2) 

or zip tuples like:

Tuple2 ((+ 1), (*2)) <*> Tuple2 (1, 10) 

Comments

4

The uniplate package provides the descend function in the Data.Generics.Uniplate.Data module. This function will apply the function everywhere the types match, so can be applied to lists, tuples, Either, or most other data types. Some examples:

descend (\x -> 2*x) (1,2) == (2,4) descend (\x -> 2*x) (1,"test",Just 2) == (2,"test",Just 4) descend (\x -> 2*x) (1,2,3,4,5) == (2,4,6,8,10) descend (\x -> 2*x) [1,2,3,4,5] == [2,4,6,8,10] 

Comments

3

Yes, you would do:

map (\x -> (fst x *2, snd x *2)) [(1,2)] 

fst grabs the first data entry in a tuple, and snd grabs the second; so, the line of code says "take a tuple, and return another tuple with the first and second items double the previous."

3 Comments

Your code does not work, but as a side note remember that you can pattern match at the arguments to a lambda function: \(x,y) -> (x*2,y*2)
@danr - I was missing a parenthesis, thanks for pointing it out :P. I wasn't aware of pattern matching though, thanks!
I don't see any reason to use map on a list with a single element: the point here is the function in the parentheses in fact.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.