7

Possible Duplicate:
Making (a, a) a Functor

I wrote the following implementation of quicksort:

import Data.List (partition)

quicksort [] = []

quicksort (x:xs) =
    let (smaller, notSmaller) = partition (< x) xs
    in  quicksort smaller ++ x : quicksort notSmaller

Then I wanted to abbreviate the two recursive calls to quicksort by applying fmap to the list pair:

quicksort (x:xs) =
    let (smaller, notSmaller) = fmap quicksort $ partition (< x) xs
    in  smaller ++ x : notSmaller

But apparently, (a, a) is not a functor. Why is that? I tried to provide one:

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

But ghci did not like my attempt:

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

Could anyone explain that error to me? I tried various "fixes", but none of them worked.

Is it possible to make (a, a) an instance of Functor? Or is the type system not expressive enough?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
fredoverflow
  • 256,549
  • 94
  • 388
  • 662

1 Answers1

14

It's important to realise that it's not (a,a) that would be the functor, in the same way that Maybe a and [a] aren't functors. Instead, the functors are Maybe and [].

A full explanation requires introducing the concept of kinds, which are like "types of types". Any concrete type has kind *. A type constructor like Maybe or [] takes a type and returns another type, so it has kind * -> *.

What's the kind of (,) (the constructor for pairs)? It needs two types, one for the first slot and one for the second slot, so it has kind * -> * -> *.

You can only make a functor out of things of kind * -> *, so the short answer to your question is no, you can't make (,) into a functor.

However, you can get around the limitation by wrapping the type. For example

newtype Pair a = P (a,a)

instance Functor Pair where
    fmap f (P (x,y)) = P (f x, f y)

The newtype wrapper will be optimized away by the compiler, so this isn't any more expensive than what you were trying to do originally - it's just a little more verbose.

Chris Taylor
  • 46,912
  • 15
  • 110
  • 154
  • Aha, I tried `type Pair a = (a, a)` and that didn't work. – fredoverflow Oct 06 '12 at 11:57
  • @FredOverflow `type` is just similar to `typedef` in C++; it doesn't create a new type, just an alias (so it wouldn't make any difference). – qox Oct 06 '12 at 12:02