Question

I'm eyeing functors, applicative functors… I'm not sure how to get where I want, but I have the feeling that following the types should get me closer.

Is there a simple way to make a map-alike which only applies to the first element of a 2-tuple? Taking first from Control.Arrow and using Arrow (->), this does the trick nicely:

map . first :: (b -> c) -> [(b, d)] -> [(c, d)]

My only concern is that I have yet to gain a real intuition for arrows, and so I'll probably find myself in deep water sooner or later if I keep this up. Plus, this seems to be a rather convenient case that can't be generalised.

Can I get the same functionality by using something from functors, monads or whatever else, while getting to the heart of what I want? I was toying around with

\f -> map (f `on` fst)

-like ideas, but couldn't quite get there.

Was it helpful?

Solution

Arrows have nice combinators for operating on tuples. You can almost think of them as the missing tuple functions!

So e.g.

> :t \f -> map (f *** id)
  :: (b -> c) -> [(b, c')] -> [(c, c')]

is a useful way for mapping over the first component.

OTHER TIPS

Another abstraction that can do this kind of thing is a bifunctor. Edward Kmett has a package called bifunctors. Data.Bifunctor has a type class for exactly this functionality and it includes in instance for 2-tuples.

So you're looking for a function of type (a -> b) -> (a,c) -> (b,c). It would be great if you could just write

{-# LANGUAGE TupleSections #-}
instance Functor (,c) where
    fmap f (x,c) = (f x, c)

but unfortunately tuple sections only work on the value level. I don't know if there's a theoretical reason for this; I suppose it messes up higher-order type unification.

Hayoo came up with the package Data.Tuple.HT where the function is called mapFst.

Without going up to bifunctors (the BiFunctor instance for (,) really does what you want and nothing more) or arrows, there's no way to get what you want, that I'm aware of at least.

I think the problem is that there are too many ways -- I think I would go with Daniel Wagner's advice -- but here's another for your amusement:

{-#LANGUAGE DeriveFunctor, MultiParamTypeClasses  #-}
import Control.Newtype

newtype P a b = P {p:: (b, a)} deriving (Show,Eq,Ord,Functor)
instance Newtype (P a b) (b,a) where pack = P; unpack = p

 -- *Main> fmap even ("Hi",4)
 -- ("Hi",True)
 -- *Main> map (fmap even) [("Hi",4),("Bye",5)]
 -- [("Hi",True),("Bye",False)]
 -- *Main> under P (fmap even) (4,"Hi")
 -- (True,"Hi")
 -- *Main> map (under P (fmap even) ) [(4,"Hi"),(5,"Bye")]
 -- [(True,"Hi"),(False,"Bye")]

Well, there is the BiFunctor package.

Or you could use a reversed pair type:

data Flip a b = Flip b a

instance Functor (Flip a) where
  fmap f (Flip x y) = Flip (f x) y
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top