{-# LANGUAGE CPP #-}
module Data.UnitsOfMeasure.Plugin.Convert
( UnitDefs(..)
, unitKind
, isUnitKind
, normaliseUnit
, reifyUnit
) where
import TyCon
import Type
#if __GLASGOW_HASKELL__ > 802
import TcType ()
#else
import TcType
#endif
#if __GLASGOW_HASKELL__ > 710
import TyCoRep
#else
import TypeRep
#endif
import Data.List
import Data.UnitsOfMeasure.Plugin.NormalForm
data UnitDefs = UnitDefs
{ unitKindCon :: TyCon
, unitBaseTyCon :: TyCon
, unitOneTyCon :: TyCon
, mulTyCon :: TyCon
, divTyCon :: TyCon
, expTyCon :: TyCon
, unpackTyCon :: TyCon
, unitSyntaxTyCon :: TyCon
, unitSyntaxPromotedDataCon :: TyCon
, equivTyCon :: TyCon
}
unitKind :: UnitDefs -> Kind
unitKind uds = TyConApp (promoteTyCon $ unitKindCon uds) []
isUnitKind :: UnitDefs -> Kind -> Bool
isUnitKind uds ty | Just (tc, _) <- tcSplitTyConApp_maybe ty = tc == unitKindCon uds
| otherwise = False
normaliseUnit :: UnitDefs -> Type -> Maybe NormUnit
normaliseUnit uds ty | Just ty1 <- coreView ty = normaliseUnit uds ty1
normaliseUnit _ (TyVarTy v) = pure $ varUnit v
normaliseUnit uds (TyConApp tc tys)
| tc == unitOneTyCon uds = pure one
| tc == unitBaseTyCon uds, [x] <- tys = pure $ baseUnit x
| tc == mulTyCon uds, [u, v] <- tys = (*:) <$> normaliseUnit uds u <*> normaliseUnit uds v
| tc == divTyCon uds, [u, v] <- tys = (/:) <$> normaliseUnit uds u <*> normaliseUnit uds v
| tc == expTyCon uds, [u, n] <- tys, Just i <- isNumLitTy n = (^:) <$> normaliseUnit uds u <*> pure i
| isFamilyTyCon tc = pure $ famUnit tc tys
normaliseUnit _ _ = Nothing
reifyUnit :: UnitDefs -> NormUnit -> Type
reifyUnit uds u | null xs && null ys = oneTy
| null ys = foldr1 times xs
| null xs = oneTy `divide` foldr1 times ys
| otherwise = foldr1 times xs `divide` foldr1 times ys
where
(pos, neg) = partition ((> 0) . snd) $ ascending u
xs = map fromAtom pos
ys = map (fromAtom . fmap negate) neg
oneTy = mkTyConApp (unitOneTyCon uds) []
times x y = mkTyConApp (mulTyCon uds) [x, y]
divide x y = mkTyConApp (divTyCon uds) [x, y]
fromAtom (a, n) = pow n (reifyAtom a)
pow 1 ty = ty
pow n ty = mkTyConApp (expTyCon uds) [ty, mkNumLitTy n]
reifyAtom (BaseAtom s) = mkTyConApp (unitBaseTyCon uds) [s]
reifyAtom (VarAtom v) = mkTyVarTy v
reifyAtom (FamAtom f tys) = mkTyConApp f tys
#if __GLASGOW_HASKELL__ > 710
promoteTyCon :: TyCon -> TyCon
promoteTyCon = id
#endif