Generalised derived instances for newtypes

The Glasgow Haskell Compiler User's Guide, Version 5.04
Prev | Chapter 7. GHC Language Features | Next

# 7.9. Generalised derived instances for newtypes

When you define an abstract type using `newtype`, you may want the new type to inherit some instances from its representation. In Haskell 98, you can inherit instances of `Eq`, `Ord`, `Enum` and `Bounded` by deriving them, but for any other classes you have to write an explicit instance declaration. For example, if you define

      newtype Dollars = Dollars Int

and you want to use arithmetic on `Dollars`, you have to explicitly define an instance of `Num`:

      instance Num Dollars where
        Dollars a + Dollars b = Dollars (a+b)
        ...

All the instance does is apply and remove the `newtype` constructor. It is particularly galling that, since the constructor doesn't appear at run-time, this instance declaration defines a dictionary which is wholly equivalent to the `Int` dictionary, only slower!

## 7.9.1. Generalising the deriving clause

GHC now permits such instances to be derived instead, so one can write

      newtype Dollars = Dollars Int deriving (Eq,Show,Num)

and the implementation uses the same `Num` dictionary for `Dollars` as for `Int`. Notionally, the compiler derives an instance declaration of the form

      instance Num Int => Num Dollars

which just adds or removes the `newtype` constructor according to the type.

We can also derive instances of constructor classes in a similar way. For example, suppose we have implemented state and failure monad transformers, such that

      instance Monad m => Monad (State s m)
      instance Monad m => Monad (Failure m)

In Haskell 98, we can define a parsing monad by

      type Parser tok m a = State [tok] (Failure m) a

which is automatically a monad thanks to the instance declarations above. With the extension, we can make the parser type abstract, without needing to write an instance of class `Monad`, via

      newtype Parser tok m a = Parser (State [tok] (Failure m) a)
                             deriving Monad

In this case the derived instance declaration is of the form

      instance Monad (State [tok] (Failure m)) => Monad (Parser tok m)

Notice that, since `Monad` is a constructor class, the instance is a partial application of the new type, not the entire left hand side. We can imagine that the type declaration is ``eta-converted'' to generate the context of the instance declaration.

We can even derive instances of multi-parameter classes, provided the newtype is the last class parameter. In this case, a ``partial application'' of the class appears in the `deriving` clause. For example, given the class

      class StateMonad s m | m -> s where ...
      instance Monad m => StateMonad s (State s m) where ...

then we can derive an instance of `StateMonad` for `Parser`s by

      newtype Parser tok m a = Parser (State [tok] (Failure m) a)
                             deriving (Monad, StateMonad [tok])

The derived instance is obtained by completing the application of the class to the new type:

      instance StateMonad [tok] (State [tok] (Failure m)) =>
               StateMonad [tok] (Parser tok m)

As a result of this extension, all derived instances in newtype declarations are treated uniformly (and implemented just by reusing the dictionary for the representation type), except `Show` and `Read`, which really behave differently for the newtype and its representation.

## 7.9.2. A more precise specification

Derived instance declarations are constructed as follows. Consider the declaration (after expansion of any type synonyms)

      newtype T v1...vn = T' (S t1...tk vk+1...vn) deriving (c1...cm)

where `S` is a type constructor, `t1...tk` are types, `vk+1...vn` are type variables which do not occur in any of the `ti`, and the `ci` are partial applications of classes of the form `C t1'...tj'`. The derived instance declarations are, for each `ci`,

      instance ci (S t1...tk vk+1...v) => ci (T v1...vp)

where `p` is chosen so that `T v1...vp` is of the right kind for the last parameter of class `Ci`.

As an example which does not work, consider

      newtype NonMonad m s = NonMonad (State s m s) deriving Monad

Here we cannot derive the instance

      instance Monad (State s m) => Monad (NonMonad m)

because the type variable `s` occurs in `State s m`, and so cannot be "eta-converted" away. It is a good thing that this `deriving` clause is rejected, because `NonMonad m` is not, in fact, a monad --- for the same reason. Try defining `>>=` with the correct type: you won't be able to.

Notice also that the order of class parameters becomes important, since we can only derive instances for the last one. If the `StateMonad` class above were instead defined as

      class StateMonad m s | m -> s where ...

then we would not have been able to derive an instance for the `Parser` type above. We hypothesise that multi-parameter classes usually have one "main" parameter for which deriving new instances is most interesting.

Prev | Home | Next
Generic classes | Up | Concurrent and Parallel Haskell