{-# LANGUAGE TemplateHaskell #-}
module Data.Units.Base.TH
(
mkUnit
, mkUnitNoFactor
, mkBaseUnit
, mkDim
, mkPrefix
)
where
import GHC.TypeError
import Language.Haskell.TH
import Data.Type.Int
import Data.Units.Base.System
import Data.Units.Base.Convert
import Data.Units.Base.Prefix
deriveList :: [Name]
deriveList :: [Name]
deriveList =
[''Eq
, ''Ord
, ''Num
, ''Fractional
, ''Floating
, ''Real
, ''RealFrac
, ''RealFloat
]
deriveListUnit :: [Name]
deriveListUnit :: [Name]
deriveListUnit = ''Show Name -> [Name] -> [Name]
forall a. a -> [a] -> [a]
: [Name]
deriveList
mkUnitNewtype :: Quote m
=> [Name] -> Name -> m Dec
mkUnitNewtype :: forall (m :: * -> *). Quote m => [Name] -> Name -> m Dec
mkUnitNewtype [Name]
l Name
unitName =
let a :: m Type
a = Name -> m Type
forall (m :: * -> *). Quote m => Name -> m Type
varT (String -> Name
mkName String
"a") in
m Cxt
-> Name
-> [TyVarBndr BndrVis]
-> Maybe Type
-> m Con
-> [m DerivClause]
-> m Dec
forall (m :: * -> *).
Quote m =>
m Cxt
-> Name
-> [TyVarBndr BndrVis]
-> Maybe Type
-> m Con
-> [m DerivClause]
-> m Dec
newtypeD
([m Type] -> m Cxt
forall (m :: * -> *). Quote m => [m Type] -> m Cxt
cxt [])
Name
unitName
[Name -> BndrVis -> TyVarBndr BndrVis
forall flag. Name -> flag -> TyVarBndr flag
PlainTV (String -> Name
mkName String
"a") BndrVis
BndrReq]
Maybe Type
forall a. Maybe a
Nothing
(Name -> [m BangType] -> m Con
forall (m :: * -> *). Quote m => Name -> [m BangType] -> m Con
normalC Name
unitName
[m Bang -> m Type -> m BangType
forall (m :: * -> *). Quote m => m Bang -> m Type -> m BangType
bangType (m SourceUnpackedness -> m SourceStrictness -> m Bang
forall (m :: * -> *).
Quote m =>
m SourceUnpackedness -> m SourceStrictness -> m Bang
bang m SourceUnpackedness
forall (m :: * -> *). Quote m => m SourceUnpackedness
noSourceUnpackedness m SourceStrictness
forall (m :: * -> *). Quote m => m SourceStrictness
noSourceStrictness) m Type
a])
[ Maybe DerivStrategy -> [m Type] -> m DerivClause
forall (m :: * -> *).
Quote m =>
Maybe DerivStrategy -> [m Type] -> m DerivClause
derivClause Maybe DerivStrategy
forall a. Maybe a
Nothing (Name -> m Type
forall (m :: * -> *). Quote m => Name -> m Type
conT (Name -> m Type) -> [Name] -> [m Type]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Name]
l)]
mkIsUnitInstance :: Quote m => Name -> Name -> m [Dec]
mkIsUnitInstance :: forall (m :: * -> *). Quote m => Name -> Name -> m [Dec]
mkIsUnitInstance Name
unitName Name
dimName = [d|
instance IsUnit $(Name -> m Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
unitName) where
type DimOf $(Name -> m Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
unitName) = $(Name -> m Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
dimName)
|]
mkShowUnitInstance :: Quote m => Name -> String -> String -> m [Dec]
mkShowUnitInstance :: forall (m :: * -> *).
Quote m =>
Name -> String -> String -> m [Dec]
mkShowUnitInstance Name
unitName String
unitStr String
prettyStr = [d|
instance ShowUnit $(Name -> m Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
unitName) where
type ShowUnitType $(Name -> m Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
unitName) = Text $(Type -> m Type
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (TyLit -> Type
LitT (String -> TyLit
StrTyLit String
prettyStr)))
showUnit = $(Lit -> m Exp
forall (m :: * -> *). Quote m => Lit -> m Exp
litE (String -> Lit
StringL String
unitStr))
prettyUnit = $(Lit -> m Exp
forall (m :: * -> *). Quote m => Lit -> m Exp
litE (String -> Lit
StringL String
prettyStr))
|]
mkConvFactorInstance :: Quote m => Name -> Rational -> m [Dec]
mkConvFactorInstance :: forall (m :: * -> *). Quote m => Name -> Rational -> m [Dec]
mkConvFactorInstance Name
unitName Rational
fctr = [d|
instance Fractional a => ConversionFactor $(Name -> m Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
unitName) a where
factor = $(Lit -> m Exp
forall (m :: * -> *). Quote m => Lit -> m Exp
litE (Rational -> Lit
RationalL Rational
fctr))
|]
mkDefaultSigConvertibleInstance :: Quote m => Name -> m [Dec]
mkDefaultSigConvertibleInstance :: forall (m :: * -> *). Quote m => Name -> m [Dec]
mkDefaultSigConvertibleInstance Name
unitName = [d|
instance Fractional a => ConvertibleUnit $(Name -> m Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
unitName) a
|]
mkNormalConvertibleInstance :: Quote m => Name -> m [Dec]
mkNormalConvertibleInstance :: forall (m :: * -> *). Quote m => Name -> m [Dec]
mkNormalConvertibleInstance Name
unitName = [d|
instance Fractional a => ConvertibleUnit $(Name -> m Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
unitName) a where
toBaseUnit = coerce
{-# INLINE toBaseUnit #-}
fromBaseUnit = coerce
{-# INLINE fromBaseUnit #-}
|]
mkUnit :: String -> String -> Name -> Rational -> Q [Dec]
mkUnit :: String -> String -> Name -> Rational -> Q [Dec]
mkUnit String
unitStr String
prettyStr Name
dimName Rational
fctr = do
let unitName :: Name
unitName = String -> Name
mkName String
unitStr
Dec
newtypeDec <- [Name] -> Name -> Q Dec
forall (m :: * -> *). Quote m => [Name] -> Name -> m Dec
mkUnitNewtype [Name]
deriveListUnit Name
unitName
[Dec]
isUnitDec <- Name -> Name -> Q [Dec]
forall (m :: * -> *). Quote m => Name -> Name -> m [Dec]
mkIsUnitInstance Name
unitName Name
dimName
[Dec]
showUnitDec <- Name -> String -> String -> Q [Dec]
forall (m :: * -> *).
Quote m =>
Name -> String -> String -> m [Dec]
mkShowUnitInstance Name
unitName String
unitStr String
prettyStr
[Dec]
convFactorDec <- Name -> Rational -> Q [Dec]
forall (m :: * -> *). Quote m => Name -> Rational -> m [Dec]
mkConvFactorInstance Name
unitName Rational
fctr
[Dec]
convUnitDec <-
if Rational
fctr Rational -> Rational -> Bool
forall a. Eq a => a -> a -> Bool
== Rational
1 then
Name -> Q [Dec]
forall (m :: * -> *). Quote m => Name -> m [Dec]
mkNormalConvertibleInstance Name
unitName
else
Name -> Q [Dec]
forall (m :: * -> *). Quote m => Name -> m [Dec]
mkDefaultSigConvertibleInstance Name
unitName
[Dec] -> Q [Dec]
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Dec] -> Q [Dec]) -> [Dec] -> Q [Dec]
forall a b. (a -> b) -> a -> b
$
[Dec
newtypeDec] [Dec] -> [Dec] -> [Dec]
forall a. [a] -> [a] -> [a]
++ [Dec]
isUnitDec [Dec] -> [Dec] -> [Dec]
forall a. [a] -> [a] -> [a]
++ [Dec]
showUnitDec [Dec] -> [Dec] -> [Dec]
forall a. [a] -> [a] -> [a]
++ [Dec]
convFactorDec [Dec] -> [Dec] -> [Dec]
forall a. [a] -> [a] -> [a]
++ [Dec]
convUnitDec
mkUnitNoFactor :: String -> String -> Name -> Q [Dec]
mkUnitNoFactor :: String -> String -> Name -> Q [Dec]
mkUnitNoFactor String
unitStr String
prettyStr Name
dimName = do
let unitName :: Name
unitName = String -> Name
mkName String
unitStr
Dec
newtypeDec <- [Name] -> Name -> Q Dec
forall (m :: * -> *). Quote m => [Name] -> Name -> m Dec
mkUnitNewtype [Name]
deriveListUnit Name
unitName
[Dec]
isUnitDec <- Name -> Name -> Q [Dec]
forall (m :: * -> *). Quote m => Name -> Name -> m [Dec]
mkIsUnitInstance Name
unitName Name
dimName
[Dec]
showUnitDec <- Name -> String -> String -> Q [Dec]
forall (m :: * -> *).
Quote m =>
Name -> String -> String -> m [Dec]
mkShowUnitInstance Name
unitName String
unitStr String
prettyStr
[Dec] -> Q [Dec]
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Dec] -> Q [Dec]) -> [Dec] -> Q [Dec]
forall a b. (a -> b) -> a -> b
$ [Dec
newtypeDec] [Dec] -> [Dec] -> [Dec]
forall a. [a] -> [a] -> [a]
++ [Dec]
isUnitDec [Dec] -> [Dec] -> [Dec]
forall a. [a] -> [a] -> [a]
++ [Dec]
showUnitDec
mkBaseUnit :: String -> String -> Name -> Q [Dec]
mkBaseUnit :: String -> String -> Name -> Q [Dec]
mkBaseUnit String
unitStr String
prettyStr Name
dimName = do
let unitName :: Name
unitName = String -> Name
mkName String
unitStr
[Dec]
unitDec <- String -> String -> Name -> Rational -> Q [Dec]
mkUnit String
unitStr String
prettyStr Name
dimName Rational
1
[Dec]
isDimDec <- Name -> Name -> Q [Dec]
forall (m :: * -> *). Quote m => Name -> Name -> m [Dec]
mkIsDimInstance Name
dimName Name
unitName
[Dec] -> Q [Dec]
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Dec] -> Q [Dec]) -> [Dec] -> Q [Dec]
forall a b. (a -> b) -> a -> b
$ [Dec]
unitDec [Dec] -> [Dec] -> [Dec]
forall a. [a] -> [a] -> [a]
++ [Dec]
isDimDec
deriveListDim :: [Name]
deriveListDim :: [Name]
deriveListDim = [Name]
deriveListUnit
mkDimNewtype :: Quote m
=> [Name] -> Name -> m Dec
mkDimNewtype :: forall (m :: * -> *). Quote m => [Name] -> Name -> m Dec
mkDimNewtype = [Name] -> Name -> m Dec
forall (m :: * -> *). Quote m => [Name] -> Name -> m Dec
mkUnitNewtype
mkIsDimInstance :: Quote m => Name -> Name -> m [Dec]
mkIsDimInstance :: forall (m :: * -> *). Quote m => Name -> Name -> m [Dec]
mkIsDimInstance Name
dimName Name
unitName = [d|
instance IsDim $(Name -> m Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
dimName) where
type DimToUnit $(Name -> m Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
dimName) = $(Name -> m Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
unitName)
|]
mkDimIdTypeInstance :: Quote m => Name -> Integer -> m [Dec]
mkDimIdTypeInstance :: forall (m :: * -> *). Quote m => Name -> Integer -> m [Dec]
mkDimIdTypeInstance Name
dimName Integer
n = [d|
type instance DimId $(Name -> m Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
dimName) = Pos $(m TyLit -> m Type
forall (m :: * -> *). Quote m => m TyLit -> m Type
litT (Integer -> m TyLit
forall (m :: * -> *). Quote m => Integer -> m TyLit
numTyLit Integer
n))
|]
mkShowDimInstance :: Quote m => Name -> String -> String -> m [Dec]
mkShowDimInstance :: forall (m :: * -> *).
Quote m =>
Name -> String -> String -> m [Dec]
mkShowDimInstance Name
dimName String
dimStr String
prettyStr = [d|
instance ShowDim $(Name -> m Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
dimName) where
type ShowDimType $(Name -> m Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
dimName) = Text $(Type -> m Type
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (TyLit -> Type
LitT (String -> TyLit
StrTyLit String
prettyStr)))
showDim = $(Lit -> m Exp
forall (m :: * -> *). Quote m => Lit -> m Exp
litE (String -> Lit
StringL String
dimStr))
prettyDim = $(Lit -> m Exp
forall (m :: * -> *). Quote m => Lit -> m Exp
litE (String -> Lit
StringL String
prettyStr))
|]
mkDim :: String -> String -> Integer -> Q [Dec]
mkDim :: String -> String -> Integer -> Q [Dec]
mkDim String
dimStr String
prettyStr Integer
n = do
let dimName :: Name
dimName = String -> Name
mkName String
dimStr
Dec
newtypeDec <- [Name] -> Name -> Q Dec
forall (m :: * -> *). Quote m => [Name] -> Name -> m Dec
mkDimNewtype [Name]
deriveListDim Name
dimName
[Dec]
dimIdDec <- Name -> Integer -> Q [Dec]
forall (m :: * -> *). Quote m => Name -> Integer -> m [Dec]
mkDimIdTypeInstance Name
dimName Integer
n
[Dec]
showDimDec <- Name -> String -> String -> Q [Dec]
forall (m :: * -> *).
Quote m =>
Name -> String -> String -> m [Dec]
mkShowDimInstance Name
dimName String
dimStr String
prettyStr
[Dec] -> Q [Dec]
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Dec] -> Q [Dec]) -> [Dec] -> Q [Dec]
forall a b. (a -> b) -> a -> b
$ [Dec
newtypeDec] [Dec] -> [Dec] -> [Dec]
forall a. [a] -> [a] -> [a]
++ [Dec]
dimIdDec [Dec] -> [Dec] -> [Dec]
forall a. [a] -> [a] -> [a]
++ [Dec]
showDimDec
mkPrefixNewtype :: Monad m => [Name] -> Name -> m Dec
mkPrefixNewtype :: forall (m :: * -> *). Monad m => [Name] -> Name -> m Dec
mkPrefixNewtype [Name]
l Name
prefixName = Dec -> m Dec
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Dec -> m Dec) -> Dec -> m Dec
forall a b. (a -> b) -> a -> b
$
Cxt
-> Name
-> [TyVarBndr BndrVis]
-> Maybe Type
-> Con
-> [DerivClause]
-> Dec
NewtypeD
[]
Name
prefixName
[ Name -> BndrVis -> Type -> TyVarBndr BndrVis
forall flag. Name -> flag -> Type -> TyVarBndr flag
KindedTV (String -> Name
mkName String
"u") BndrVis
BndrReq (Name -> Type
ConT ''Unit)
, Name -> BndrVis -> TyVarBndr BndrVis
forall flag. Name -> flag -> TyVarBndr flag
PlainTV (String -> Name
mkName String
"a") BndrVis
BndrReq
]
Maybe Type
forall a. Maybe a
Nothing
(Name -> [BangType] -> Con
NormalC Name
prefixName
[ ( SourceUnpackedness -> SourceStrictness -> Bang
Bang SourceUnpackedness
NoSourceUnpackedness SourceStrictness
NoSourceStrictness
, Type -> Type -> Type
AppT (Name -> Type
VarT (String -> Name
mkName String
"u")) (Name -> Type
VarT (String -> Name
mkName String
"a"))
)
])
[ Maybe DerivStrategy -> Cxt -> DerivClause
DerivClause
(DerivStrategy -> Maybe DerivStrategy
forall a. a -> Maybe a
Just DerivStrategy
NewtypeStrategy)
((Name -> Type) -> [Name] -> Cxt
forall a b. (a -> b) -> [a] -> [b]
map Name -> Type
ConT [Name]
l)
, Maybe DerivStrategy -> Cxt -> DerivClause
DerivClause
(DerivStrategy -> Maybe DerivStrategy
forall a. a -> Maybe a
Just (Type -> DerivStrategy
ViaStrategy Type
viaType))
[Name -> Type
ConT ''Show]
, Maybe DerivStrategy -> Cxt -> DerivClause
DerivClause
(DerivStrategy -> Maybe DerivStrategy
forall a. a -> Maybe a
Just (Type -> DerivStrategy
ViaStrategy Type
viaTypeNoA))
[Name -> Type
ConT ''ShowUnit]
]
where
viaType :: Type
viaType =
(Type -> Type -> Type) -> Type -> Cxt -> Type
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl Type -> Type -> Type
AppT (Name -> Type
ConT ''MetaPrefix)
[ Name -> Type
ConT Name
prefixName
, Name -> Type
VarT (String -> Name
mkName String
"u")
, Name -> Type
VarT (String -> Name
mkName String
"a")
]
viaTypeNoA :: Type
viaTypeNoA =
(Type -> Type -> Type) -> Type -> Cxt -> Type
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl Type -> Type -> Type
AppT (Name -> Type
ConT ''MetaPrefix)
[ Name -> Type
ConT Name
prefixName
, Name -> Type
VarT (String -> Name
mkName String
"u")
]
mkPrefixIsUnitInstance :: Name -> Q [Dec]
mkPrefixIsUnitInstance :: Name -> Q [Dec]
mkPrefixIsUnitInstance Name
prefixName = [d|
deriving via MetaPrefix $(Name -> Q Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
prefixName) u instance IsUnit u => IsUnit ($(Name -> Q Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
prefixName) u)
|]
mkShowPrefixInstance :: Quote m => Name -> String -> String -> m [Dec]
mkShowPrefixInstance :: forall (m :: * -> *).
Quote m =>
Name -> String -> String -> m [Dec]
mkShowPrefixInstance Name
prefixName String
prefixStr String
prettyStr = [d|
instance ShowPrefix $(Name -> m Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
prefixName) where
type ShowPrefixType $(Name -> m Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
prefixName) = Text $(Type -> m Type
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (TyLit -> Type
LitT (String -> TyLit
StrTyLit String
prettyStr)))
showPrefix = $(Lit -> m Exp
forall (m :: * -> *). Quote m => Lit -> m Exp
litE (String -> Lit
StringL String
prefixStr))
prettyPrefix = $(Lit -> m Exp
forall (m :: * -> *). Quote m => Lit -> m Exp
litE (String -> Lit
StringL String
prettyStr))
|]
mkPrefixFactorInstance :: Name -> Rational -> Q [Dec]
mkPrefixFactorInstance :: Name -> Rational -> Q [Dec]
mkPrefixFactorInstance Name
prefixName Rational
fctr = [d|
instance Fractional a => PrefixFactor $(Name -> Q Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
prefixName) a where
prefixFactor = $(Lit -> Q Exp
forall (m :: * -> *). Quote m => Lit -> m Exp
litE (Rational -> Lit
RationalL Rational
fctr))
{-# INLINE prefixFactor #-}
|]
mkPrefixConvFactorInstance :: Name -> Q [Dec]
mkPrefixConvFactorInstance :: Name -> Q [Dec]
mkPrefixConvFactorInstance Name
prefixName = [d|
instance ConversionFactor u a => ConversionFactor ($(Name -> Q Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
prefixName) u) a where
factor = factor @(MetaPrefix $(Name -> Q Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
prefixName) u)
{-# INLINE factor #-}
|]
mkPrefixConvUnitInstance :: Name -> Q [Dec]
mkPrefixConvUnitInstance :: Name -> Q [Dec]
mkPrefixConvUnitInstance Name
prefixName = [d|
instance (ConvertibleUnit u a, Fractional a)
=> ConvertibleUnit ($(Name -> Q Type
forall (m :: * -> *). Quote m => Name -> m Type
conT Name
prefixName) u) a where
toBaseUnit = prefixToBaseUnit
{-# INLINE toBaseUnit #-}
fromBaseUnit = prefixFromBaseUnit
{-# INLINE fromBaseUnit #-}
|]
mkPrefix :: String -> String -> Rational -> Q [Dec]
mkPrefix :: String -> String -> Rational -> Q [Dec]
mkPrefix String
prefixStr String
prettyStr Rational
fctr = do
let prefixName :: Name
prefixName = String -> Name
mkName String
prefixStr
Dec
newtypeDec <- [Name] -> Name -> Q Dec
forall (m :: * -> *). Monad m => [Name] -> Name -> m Dec
mkPrefixNewtype [Name]
deriveList Name
prefixName
[Dec]
isUnitDec <- Name -> Q [Dec]
mkPrefixIsUnitInstance Name
prefixName
[Dec]
showPrefixDec <- Name -> String -> String -> Q [Dec]
forall (m :: * -> *).
Quote m =>
Name -> String -> String -> m [Dec]
mkShowPrefixInstance Name
prefixName String
prefixStr String
prettyStr
[Dec]
factorDec <- Name -> Rational -> Q [Dec]
mkPrefixFactorInstance Name
prefixName Rational
fctr
[Dec]
convFactorDec <- Name -> Q [Dec]
mkPrefixConvFactorInstance Name
prefixName
[Dec]
convUnitDec <- Name -> Q [Dec]
mkPrefixConvUnitInstance Name
prefixName
[Dec] -> Q [Dec]
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Dec] -> Q [Dec]) -> [Dec] -> Q [Dec]
forall a b. (a -> b) -> a -> b
$ [Dec
newtypeDec] [Dec] -> [Dec] -> [Dec]
forall a. [a] -> [a] -> [a]
++ [Dec]
isUnitDec [Dec] -> [Dec] -> [Dec]
forall a. [a] -> [a] -> [a]
++ [Dec]
showPrefixDec
[Dec] -> [Dec] -> [Dec]
forall a. [a] -> [a] -> [a]
++ [Dec]
factorDec [Dec] -> [Dec] -> [Dec]
forall a. [a] -> [a] -> [a]
++ [Dec]
convFactorDec [Dec] -> [Dec] -> [Dec]
forall a. [a] -> [a] -> [a]
++ [Dec]
convUnitDec