In Haskell, the Functor instance of (,) is apparently
instance Functor (,) where
fmap f (a,b) = (a,f b)
This results in the unintuitive fact that:
> fmap (const 5) [1, 2]
[5,5]
> fmap (const 5) (1, 2)
(1,5)
Now, using this definiton would work much better, in my opinion:
instance Functor (,) where
fmap f (a,b) = (f a,f b)
It would work like this:
> fmap (const 5) (1, 2)
(5,5)
Why is it not like this?
The instance is actually not instance Functor (,)
like you said. That wouldn't be well-kinded:
Prelude> :k Functor
Functor :: (* -> *) -> Constraint
Prelude> :k (,)
(,) :: * -> * -> *
i.e., (,)
takes two type arguments (the types of both tuple-fields) to construct a tuple-type, but the Functor
class is actually for type-constructors that take only one argument, like
Prelude> :k []
[] :: * -> *
Prelude> :k IO
IO :: * -> *
So why is there a functor instance at all? Frankly, I think this is one of the instances which should not have been defined, precisely because it's confusing that tuples aren't symmetric. However, it can actually be defined and there's only one way to do it, so this choice by the standard libraries is certainly not unreasonable. The trick is currying: you can partially apply the (,)
constructor to any fixed left-field type, and that gives you then a single-argument type constructor. For example
Prelude> :k (,) Int
(,) Int :: * -> *
so you can for instance have
instance Functor ((,) Int)
Clearly it doesn't depend on what concrete type the left field has, so you can as well make it
instance Functor ((,) a)
In value-level Haskell, this section would be written
instance Functor (a,)
Unlike at the term level, partial application does only work for the leftmost argument (the section (,b)
would actually be be sugar for \a -> (a,b)
, but there are no type-level lambdas in Haskell), so instance Functor ((,) a)
is the only instance that's possible here.
To get the behaviour you asked for, i.e. function applied to both/all fields, you need both fields to actually have the same type. I.e., you need a type constructor that has only one argument to begin with, and just uses that type twice for the fields of its value constructor. A standard type with that behaviour is V2
.