19

How can I make (a, a) a Functor without resorting to a newtype?

Basically I want it to work like this:

instance Functor (a, a) where
  fmap f (x, y) = (f x, f y)

But of course that's not a legal way to express it:

Kind mis-match
The first argument of `Functor' should have kind `* -> *',
but `(a, a)' has kind `*'
In the instance declaration for `Functor (a, a)'

What I really want is a type-level function like this: \a -> (a, a) (invalid syntax). So a type alias, perhaps?

type V2 a = (a, a)
instance Functor V2 where
    fmap f (x, y) = (f x, f y)

I would think this would work, but it doesn't. First I get this complaint:

Illegal instance declaration for `Functor V2'
(All instance types must be of the form (T t1 ... tn)
 where T is not a synonym.
 Use -XTypeSynonymInstances if you want to disable this.)
In the instance declaration for `Functor V2'

If I follow the advice and add the TypeSynonymInstances extension, I get a new error:

Type synonym `V2' should have 1 argument, but has been given 0
In the instance declaration for `Functor V2'

Well, duh, that's the point! V2 has kind * -> * which is what is required of a Functor instance. Well, ok, I can use a newtype like this:

newtype V2 a = V2 (a, a)
instance Functor V2 where
  fmap f (V2 (x, y)) = V2 (f x, f y)

But now I've got to sprinkle V2s liberally throughout my code instead of just being able to deal with simple tuples, which kind of defeats the point of making it a Functor; at that point I might as well make my own function vmap :: (a -> b) -> (a, a) -> (b, b).

So is there any way to do this nicely, i.e. without a newtype?

Tom Crockett
  • 30,818
  • 8
  • 72
  • 90
  • When would you want to make tuples a Functor like this? It seems to me that if you need uber-Functor powers to operate on special-case tuples, you should probably be using a custom data structure, rather than tuples, in the first place. What do the tuples you are manipulating represent? – Dan Burton Jan 27 '11 at 05:17
  • 4
    @Dan I don't _need_ "uber-Functor powers", it just would've been mildly convenient, seemed like it should be possible, and if it's not I'm curious why. – Tom Crockett Jan 27 '11 at 05:34
  • 1
    @pelotom I agree that it seems like it should be possible, though it appears that it is not. I just thought I would take a moment to get on my soapbox and preach the goodness of making an expressive structure tailored to your problem, rather than overloading tuples. – Dan Burton Jan 27 '11 at 05:42
  • 2
    @pelotom: The thing you're looking for is typically called "type-level lambdas"; as you've noticed, there aren't any in Haskell. [This other StackOverflow question](http://stackoverflow.com/questions/4069840/lambda-for-type-expressions-in-haskell) has some related information. – Antal Spector-Zabusky Jan 27 '11 at 06:17
  • @Antal, thanks for the link, very informative – Tom Crockett Jan 27 '11 at 06:25
  • 1
    @DanBurton: I'm not the OP, but for example, Functor/Traversable instances for tuples would be handy for use with the [automatic differentiation library](http://hackage.haskell.org/package/ad) (say, if your functions have known low dimension and you don't want to use lists/vectors). – FunctorSalad Feb 08 '12 at 19:27
  • 1
    [`Bifunctor`](https://hackage.haskell.org/package/bifunctors-3.2.0.1/docs/Data-Bifunctor.html) is the solution, to my mind. – AJF Jul 11 '15 at 23:17

3 Answers3

16

As others have stated, there's no way to do this without resorting to newtypes or data declarations. However, have you looked at Control.Arrow? Many of those functions are very useful with tuples, for example:

vmap :: (a -> b) -> (a,a) -> (b,b)
vmap f = f *** f
John L
  • 27,937
  • 4
  • 73
  • 88
4

You can declare

instance Functor ((,) a) where
  ...

However that doesn't constrain the first element of your pair, and fmap would only act on the second element.

The issue is that a tuple doesn't enforce a relationship between the types of the two elements.

If you don't want a newtype decorator you can make your own fresh type:

data Pair a = P a a

instance Functor Pair where
  ...

which will be easier to work with than a newtype around a tuple.

Antoine Latter
  • 1,545
  • 10
  • 13
  • 1
    I don't want a functor that only acts on one of the elements of the tuple, I want a functor for `(a, a)`, which acts on both the first and second elements (because they have the same type). And I'm trying to avoid creating a new data type. – Tom Crockett Jan 27 '11 at 04:18
  • 1
    @pelotom, not possible. Functor takes a data constructor argument, `* -> *`, and `(a,a)` is not one of those. You've got to use a `newtype` or `data`. – luqui Jan 27 '11 at 05:02
  • @luqui I assume you mean they require a type constructor? That's about what I feared... but I see no reason why the type alias shouldn't work. – Tom Crockett Jan 27 '11 at 05:29
  • 3
    @pelotom, it is hard to explain. Try to write a type inferencer that can handle instances of type synonyms. It's very hard, if possible at all. – luqui Jan 27 '11 at 06:08
  • 1
    `instance Functor ((,) a)` is already in `Control.Monad.Instances` – nponeccop Oct 06 '12 at 13:22
0

With singletons you can define a Functor type class for defunctionalized symbols (Type ~> Type instead of Type -> Type)

{-# Language ExplicitNamespaces, TypeApplications, TypeOperators, KindSignatures, ScopedTypeVariables, DataKinds, TypeInType, TypeFamilies, AllowAmbiguousTypes, InstanceSigs #-}

import Data.Kind (Type)
import Data.Singletons (type (~>), Apply)

class Functor' (f :: Type ~> Type) where
  fmap' :: (a -> a') -> (Apply f a -> Apply f a')

data Dup :: Type ~> Type

type instance Dup `Apply` a = (a, a)

instance Functor' Dup where
  fmap' :: (a -> a') -> ((a, a) -> (a', a'))
  fmap' f (a1, a2) = (f a1, f a2)

This gives you a Prelude.Functor instance automatically

newtype f $ a = App (Apply f a)

instance Functor' f => Functor (($) f) where
  fmap :: (a -> a') -> (f $ a -> f $ a')
  fmap f (App fa) = App (fmap' @f f fa) 
Iceland_jack
  • 6,848
  • 7
  • 37
  • 46