Reputation: 19347
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.
Upvotes: 8
Views: 2247
Reputation: 8199
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")]
Upvotes: 2
Reputation: 6610
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.
Upvotes: 2
Reputation: 54584
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
Upvotes: 1
Reputation: 7272
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.
Upvotes: 5
Reputation: 137957
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.
Upvotes: 10