| | 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`. |