For now I have this little helper:
both f (one,two) = (f one , f two)
Then I was idly wondering whether fmap
"iterates" a tuple so I asked GHCi:
fmap reverse ("aA","bB")
Result being:
("aA","Bb")
Odd! So the semantics of fmap over a tuple seems to be "to apply func to the snd", if you will.
Anything in base
or Prelude
I should use instead of my own both
? Hoogle gave no promising results or I parsed them wrongly.
In terms of "mapping" over both elements of a tuple, the best you have is bimap :: Bifunctor p => (a -> b) -> (c -> d) -> p a c -> p b d
defined in Data.Bifunctor
(now in base
!). With that, you can write
ghci> bimap reverse reverse ("aA","bB")
("Aa","Bb")
Alternately, you can use (***) :: Arrow a => a b c -> a b' c' -> a (b, b') (c, c')
defined in Control.Arrow
(in particular using the Arrow (->)
instance). With that, you can write
ghci> (reverse *** reverse) ("aA","bB")
("Aa","Bb")
However, if you are trying to map over both elements of a tuple at the same time, perhaps you are looking for an abstraction that groups together two values of type a
. If this is the case, I would recommend you use a newtype
around (a,a)
. Then, you can turn on the -XDeriveFunctor
extension as get the following
ghci> :set -XDeriveFunctor
ghci> newtype Pair a = Pair (a,a) deriving (Show,Functor)
ghci> fmap reverse (Pair ("aA","bB"))
Pair ("Aa","Bb")
You need to consider the full type. In the type (a,b)
, which is the same a s (,) a b
, the functor is (,) a
. Hence we get
fmap :: (b -> c) -> (,) a b -> (,) a c
This makes it clear that the first component is unaffected by fmap
.
> fmap reverse (True,"bB")
(True,"Bb")
You are probably thinking of another functor
-- user-defined
data Pair a = Pair a a deriving Functor
Here we do have
fmap :: (b -> c) -> Pair b -> Pair c
affecting both components, but this type is not the regular tuple type.
Bonus puzzle: for the same reason
> length (1,2)
1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With