views:

151

answers:

3

Take a quick peek at the following interactive session in GHCi:

Prelude> import Control.Applicative
Prelude Control.Applicative> (+1) <$> [1,2]
[2,3]
Prelude Control.Applicative> (+1) <$> (1,2)
(1,3)

I guess there is a good reason for the behavior of <$> regarding pairs, but I wasn't able to find one so far, so:

why is <$> (or `fmap`) defined to act only on the second member of a pair and not on both values?

+2  A: 

I guess, a tuple doesn't need to be homogeneous, I mean both types can be different. If you want a homogeneous tuple you can use a list and then fmap will work.

How would you expect (+1) ("Hello", 2) to work ?

Prelude> import Control.Applicative
Prelude Control.Applicative> (+1) <$> ("hello",2)
("hello",3)

That's just work, but there is not special behavior when both type are same. By the way I don't know why the second value is not used rather than the first one, but anyway you can only use one value.

mb14
+12  A: 

<$> (aka fmap) is a member of the Functor class like so:

class Functor f where
  fmap :: (a -> b) -> f a -> f b

So whatever f is must be a parameterised type with one type argument. Lists are one such type, when written in their prefix form [] ([] a is the same as [a]). So the instance for lists is:

instance Functor [] where
  -- fmap :: (a -> b) -> [] a -> [] b 
  fmap = map

Pairs can also be written in prefix form: (,) a b is the same as (a, b). So let's consider what we do if we want a Functor instance involving pairs. We can't declare an instance Functor (,) because the pair constructor (,) takes two types -- and they can be different types! What we can do is declare an instance for (,) a -- that's a type that only needs one more type:

instance Functor ( (,) a ) where
  -- fmap :: (b -> c) -> (,) a b -> (,) a c
  fmap f (x, y) = (x, f y)

Hopefully you can see that the definition of fmap is the only sensible one we can give. The answer as to why the functor instance operates on the second item in a pair is that the type for the second item comes last in the list! We can't easily declare a functor instance that operates on the first item in a pair. Incidentally, this generalises to larger tuples, e.g. the quadruple (,,,) a b c d (aka (a, b, c, d)) can also have a Functor instance on the last item:

instance Functor ( (,,,) a b c) where
  -- fmap :: (d -> e) -> (,,,) a b c d -> (,,,) a b c e
  fmap f (p, q, r, s) = (p, q, r, f s)

Hope that helps explain it all!

Neil Brown
You typed faster then me... argh
FUZxxl
bug: the examples should say `instance Functor []` instead of `class`.
hzap
Thanks -- now fixed
Neil Brown
+3  A: 

Consider the definition of the Functor typeclass:

class Functor f where
  fmap :: (a -> b) -> f a -> f b

Obviously, f has kind * -> *. So you can only declare instances for a datatype, which has kind * -> *. What you can do, is doing some stuff like this:

instance Functor (,) where
  fmap :: (a -> b) -> (,) a -> (,) b

This would work on partitial applicated tuples and is really unhandy. So one defined the instance like this:

instance Functor ((,) a) where
  fmap :: (b -> c) -> (,) a b -> (,) a c
  fmap f (x,y) = (x,f y)

In a nutshell: It is not possible in plain Haskell 98 (although I believe tht there's a syntax extension for this) to define an instance as

What you can do, is defining your own tuple:

data T a = T a a

instance Functor T where
  fmap f (T a b) = T (f a) (f b)

Then you can do whatever you like. You see, because the kind is * -> * instead of * -> * -> *, anything's okay.

FUZxxl