You'll need some more extensions:
{-# LANGUAGE PolyKinds, ScopedTypeVariables #-}
PolyKinds
turns on more evil type hackery and ScopedTypeVariables
allows you to reference type variables bound in instance heads and type signatures, in the definition of a function.
Then we can write the following:
data Proxy a = Proxy
class IntRep (n :: Int) where
natToInt :: Proxy (n :: Int) -> Integer
instance IntRep Zero where
natToInt _ = 0
instance (IntRep n) => IntRep (Succ n) where
natToInt _ = 1 P.+ (natToInt (Proxy :: Proxy n))
instance (IntRep n) => IntRep (Pred n) where
natToInt _ = (natToInt (Proxy :: Proxy n)) P.- 1
Proxy
combined with PolyKinds
lets you reference n
defined in the instance declaration of IntRep
. The usual strategy for computation on phantom types is to just use undefined :: t
, but undefined
has kind *
so undefined :: Zero
is a kind mismatch. Because you defined Unit
as Unit :: Int -> Int -> Int -> *
and not Unit :: * -> * -> * -> *
this extra misdirection is required.
Finally the Show
instance:
instance (IntRep m, IntRep s, IntRep kg) => Show (Unit m s kg) where
show (U a) = unwords [show a, "m^" ++ u0, "s^" ++ u1, "kg^" ++ u2]
where u0 = show $ natToInt (Proxy :: Proxy m)
u1 = show $ natToInt (Proxy :: Proxy s)
u2 = show $ natToInt (Proxy :: Proxy kg)
and
Prelude> main
0.1 m^1 s^-1 kg^0
Additional reading: http://comments.gmane.org/gmane.comp.lang.haskell.glasgow.user/22159