3
votes

Given the following algebraic type:

ghci> data Foo a = Foo a

Then I instantiate one of them.

ghci> let f = Foo "foo"

Lastly, I'd like to call fmap to apply a function as, (a -> b) -> Foo a -> Foo b.

ghci> fmap (++ "bar") f

<interactive>:78:1:
    No instance for (Functor Foo) arising from a use of ‘fmap’
    In the expression: fmap (++ "bar") f
    In an equation for ‘it’: it = fmap (++ "bar") f

But, since I didn't implement a Functor instance of Foo, I can't use fmap.

Is there a way to get Functor instances for free? I have zero knowledge of Haskell's compiler, but perhaps it's clever enough to know that fmap on Foo a is simply apply (a -> b) to Foo's a?

2
The problem with derived functor instances is they might not work in the way you expect. When in doubt, write it yourself!AJF
@AJFarmar At least extensionally and avoiding undefined, parametricity guarantees that there is most one fmap. Performancewise, of course, some corners may remain uncut by the automatics.pigworker
@pigworker's comment boils down (well, waters down, actually) to this: Under normal circumstances (for a specific value of "normal"), there is only one valid instance Functor T for any given T, and DeriveFunctor will produce that instance. Writing the instances by hand is an excellent way to understand them, though. I've learned a lot by studying the hand-written instances in base, free, folds, and many other libraries.Christian Conkle

2 Answers

10
votes

Working in ghci, if you make the incantation

Prelude> :set -XDeriveFunctor

then the compiler will become as clever as you are hoping for, if not quite as enthusiastic. You will need to invoke the functionality, thus,

Prelude> data Foo a = Foo a deriving (Show, Functor)

(the Show is just for printing the output, below) and then you will be able to do stuff like

Prelude> fmap (++"bar") (Foo "foo")
Foo "foobar"

In a module, you achieve the same by adding the pragma

{-# LANGUAGE DeriveFunctor #-}

before the module declaration. It's good for at least the more straightforward Functor instances, but you can fool it into a false negative.

Prelude> data Boo a = Boo (Either a Bool) deriving Functor

<interactive>:9:43:
    Can't make a derived instance of ‘Functor Boo’:
      Constructor ‘Boo’ must use the type variable only as the
        last argument of a data type
      In the data declaration for ‘Boo’

Meanwhile

data Goo a = Goo (Either Bool a) deriving Functor

is ok, and the machinery has clearly been hacked to work with pairing, as

data Woo a = Woo (a, Bool) deriving Functor

is permitted.

So it's not as clever as it could be, but it's better than a poke in the eye.

4
votes

Sure it can, add deriving Functor to your data declaration and {-# LANGUAGE DeriveFunctor #-} at the top of file.