| 24 | |
| 25 | The paper describes an improved implementation of `Typeable` (section 2.5). This has not |
| 26 | yet been implemented; the current `Typeable` class is: |
| 27 | {{{ |
| 28 | class Typeable (a :: *) where |
| 29 | typeOf :: a -> TypeRep |
| 30 | }}} |
| 31 | |
| 32 | The new proposal makes it into: |
| 33 | {{{ |
| 34 | data Proxy a = Proxy |
| 35 | |
| 36 | class Typeable a where |
| 37 | typeRep :: Proxy a -> TypeRep |
| 38 | }}} |
| 39 | Note that `Proxy` is kind polymorphic, and so is the new `Typeable`: its type argument |
| 40 | `a` can have any kind `k`. The paper goes on to describe how we can then give |
| 41 | kind-specific instances: |
| 42 | {{{ |
| 43 | instance Typeable Int where typeRep _ = ... |
| 44 | |
| 45 | instance Typeable [] where typeRep _ = ... |
| 46 | }}} |
| 47 | |
| 48 | The following changes need to done in the compiler: |
| 49 | * Update `Data.Typeable` in `base` (mostly deleting classes and adding `Proxy`). |
| 50 | |
| 51 | * Rewrite the `deriving Typeable` mechanism in `TcGenDeriv`. |
| 52 | |
| 53 | From the user's perspective nothing has to change. We can make the new implementation |
| 54 | backwards-compatible by: |
| 55 | * Calling the method of `Typeable` `typeRep`, and not `typeOf` |
| 56 | * Defining `typeOf`, `typeOf1`, ..., separately |
| 57 | |
| 58 | Concretely, the new `Data.Typeable` will look something like this: |
| 59 | {{{ |
| 60 | {-# LANGUAGE ScopedTypeVariables #-} |
| 61 | {-# LANGUAGE PolyKinds #-} |
| 62 | |
| 63 | -- Type representation: unchanged |
| 64 | data TypeRep = ... |
| 65 | |
| 66 | -- Kind-polymorphic proxy |
| 67 | data Proxy t = Proxy |
| 68 | |
| 69 | -- Kind-polymorphic Typeable |
| 70 | class Typeable a where |
| 71 | typeRep :: Proxy a -> TypeRep |
| 72 | |
| 73 | -- Instances for base types |
| 74 | instance Typeable Char where ... |
| 75 | instance Typeable [] where ... |
| 76 | instance Typeable Either where ... |
| 77 | |
| 78 | -- Old methods for backwards compatibility |
| 79 | typeOf :: forall a. Typeable a => a -> TypeRep |
| 80 | typeOf x = typeRep (getType x) where |
| 81 | getType :: a -> Proxy a |
| 82 | getType _ = Proxy |
| 83 | |
| 84 | typeOf1 :: forall t (a :: *). Typeable t => t a -> TypeRep |
| 85 | typeOf1 x = typeRep (getType1 x) where |
| 86 | getType1 :: t a -> Proxy t |
| 87 | getType1 _ = Proxy |
| 88 | }}} |
| 89 | |
| 90 | This is nearly enough; remember that currently we can do things like this: |
| 91 | {{{ |
| 92 | typeOf "p" |
| 93 | typeOf1 "p" |
| 94 | }}} |
| 95 | And they mean different things: the first is the representation of `[Char]`, |
| 96 | whereas the second is the representation of `[]`. In particular, |
| 97 | `typeOf1 "p" == typeOf1 [()]`, for instance. To keep this behavior we have |
| 98 | to guarantee that a datatype `T` with type parameters `a1` through `an` gets instances: |
| 99 | {{{ |
| 100 | data T a1 ... an |
| 101 | |
| 102 | instance Typeable T |
| 103 | instance (Typeable a1) => Typeable (T a1) |
| 104 | ... |
| 105 | instance (Typeable a1, ..., Typeable an) => Typeable (T a1 ... an) |
| 106 | }}} |
| 107 | |
| 108 | We can do this as before, by defining the arity `n-1` instance from the |
| 109 | arity `n` instance: |
| 110 | |
| 111 | {{{ |
| 112 | instance (Typeable t, Typeable (a :: *)) => Typeable (t a) |
| 113 | instance (Typeable t, Typeable (a :: *), Typeable (b :: *)) => Typeable (t a b) |
| 114 | }}} |
| 115 | |
| 116 | If we're willing to use `-XUndecidableInstances`, we can even do this with |
| 117 | a single instance, relying on `-XPolyKinds`: |
| 118 | {{{ |
| 119 | instance (Typeable t, Typeable a) => Typeable (t a) |
| 120 | }}} |
| 121 | In this instance, `t` has kind `k -> *` and `a` has kind `k`. |