I had some types like this:
data Currency = USD | EUR
deriving (Show, Typeable)
data Money :: Currency -> * where
Money :: Int -> Money c
deriving (Show, Typeable)
And I wanted to use typeOf
with them in this function:
findRate :: Money a -> Rates -> Maybe Double
findRate a = M.lookup (typeOf a)
That didn't work, because the type a
in findRate didn't have a Typeable
instance. So I fixed it by doing this:
deriving instance Typeable USD
deriving instance Typeable EUR
findRate :: (Typeable a) => Money a -> Rates -> Maybe Double
However, that becomes a lot of boilerplate when the number of currencies increase. Is there a way to specify that all types of kind Currency
should derive a Typeable
instance?
EDIT: Also, a way to make it infer that in Money a
the a
is Typeable
would be nice, so then I don't need to add (Typeable a) =>
everywhere. That's minor though.
Yes, you can use the AutoDeriveTypeable
extension.
For the other part, the closest thing I could think of was to put Typeable c =>
inside the GADT definition as follows:
{-# LANGUAGE AutoDeriveTypeable #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
import Data.Typeable
import qualified Data.Map as M
type Rates = M.Map TypeRep Double
data Currency = USD | EUR
deriving (Show, Typeable)
data Money :: Currency -> * where
Money :: Typeable c => Int -> Money c
instance Show (Money c) where
show (Money n) = "Money " ++ show n
findRate :: Money a -> Rates -> Maybe Double
findRate a@(Money _) = M.lookup (typeOf a)
Note though:
a
to get the Typeable
context out of it, which typeOf
itself doesn't.Show
automatically for the GADT.