mutable-0.2.1.0: src/Data/Mutable/Internal/TH.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE TemplateHaskell #-}
module Data.Mutable.Internal.TH (
mutableTuples
, listRefTuples
) where
import Control.Monad
import Data.Generics.Product.Internal.HList
import Data.List
import Data.Mutable.Internal
import Language.Haskell.TH
tyVarNames :: [String]
tyVarNames = (:[]) <$> filter (/= 's') ['a' .. 'z']
mutableTuples :: [Int] -> Q [Dec]
mutableTuples = traverse mutableTuple
listRefTuples :: [Int] -> Q [Dec]
listRefTuples = traverse listRefTuple
mutableTuple
:: Int
-> Q Dec
mutableTuple n = do
valVars <- replicateM n (newName "x")
refVars <- replicateM n (newName "r")
-- instance (Mutable s a, Mutable s b, Mutable s c) => Mutable s (a, b, c) where
pure $ InstanceD
Nothing
(mutableS . VarT <$> tyVars)
(mutableS instHead)
[ refImpl
, thawImpl valVars
, freezeImpl refVars
, copyImpl refVars valVars
, moveImpl refVars valVars
, cloneImpl refVars
, unsafeThawImpl valVars
, unsafeFreezeImpl refVars
]
where
tuplerT :: [Type] -> Type
tuplerT = applyAllT (TupleT n)
tupConE :: Exp
tupConE = ConE (tupleDataName n)
mutableS :: Type -> Type
mutableS = ((ConT ''Mutable `AppT` VarT (mkName "s")) `AppT`)
refS :: Type -> Type
refS = ((ConT ''Ref `AppT` VarT (mkName "s")) `AppT`)
tyVars :: [Name]
tyVars = mkName <$> take n tyVarNames
instHead :: Type
instHead = tuplerT $ VarT <$> tyVars
-- type Ref s (a, b, c) = (Ref s a, Ref s b, Ref s c)
refImpl :: Dec
#if MIN_VERSION_template_haskell(2,15,0)
refImpl = TySynInstD
. TySynEqn Nothing (refS instHead)
#else
refImpl = TySynInstD ''Ref
. TySynEqn [VarT (mkName "s"), instHead]
#endif
$ tuplerT (refS . VarT <$> tyVars)
thawImpl :: [Name] -> Dec
thawImpl valVars = FunD 'thawRef [
Clause [TupP (BangP . VarP <$> valVars)]
(NormalB . liftApplyAllE tupConE $
(VarE 'thawRef `AppE`) . VarE <$> valVars
)
[]
]
-- freezeRef (u , v , w ) = (,,) <$> freezeRef u <*> freezeRef v <*> freezeRef w
freezeImpl :: [Name] -> Dec
freezeImpl refVars = FunD 'freezeRef [
Clause [TupP (VarP <$> refVars)]
(NormalB . liftApplyAllE tupConE $
(VarE 'freezeRef `AppE`) . VarE <$> refVars
)
[]
]
-- copyRef (u , v , w ) (!x, !y, !z) = copyRef u x *> copyRef v y *> copyRef w z
copyImpl :: [Name] -> [Name] -> Dec
copyImpl refVars valVars = FunD 'copyRef [
Clause [ TupP (BangP . VarP <$> refVars)
, TupP (BangP . VarP <$> valVars)
]
(NormalB . sequenceAllE $
zipWith (\r v -> (VarE 'copyRef `AppE` VarE r) `AppE` VarE v) refVars valVars
)
[]
]
-- moveRef (u , v , w ) ( x, y, z) = moveRef u x *> moveRef v y *> moveRef w z
moveImpl :: [Name] -> [Name] -> Dec
moveImpl refVars valVars = FunD 'moveRef [
Clause [ TupP (BangP . VarP <$> refVars)
, TupP (BangP . VarP <$> valVars)
]
(NormalB . sequenceAllE $
zipWith (\r v -> (VarE 'moveRef `AppE` VarE r) `AppE` VarE v) refVars valVars
)
[]
]
-- cloneRef (u , v , w ) = (,,) <$> cloneRef u <*> cloneRef v <*> cloneRef w
cloneImpl :: [Name] -> Dec
cloneImpl refVars = FunD 'cloneRef [
Clause [TupP (VarP <$> refVars)]
(NormalB . liftApplyAllE tupConE $
(VarE 'cloneRef `AppE`) . VarE <$> refVars
)
[]
]
-- unsafeThawRef (!x, !y, !z) = (,,) <$> unsafeThawRef x <*> unsafeThawRef y <*> unsafeThawRef z
unsafeThawImpl :: [Name] -> Dec
unsafeThawImpl valVars = FunD 'unsafeThawRef [
Clause [TupP (BangP . VarP <$> valVars)]
(NormalB . liftApplyAllE tupConE $
(VarE 'unsafeThawRef `AppE`) . VarE <$> valVars
)
[]
]
-- unsafeFreezeRef (u , v , w ) = (,,) <$> unsafeFreezeRef u <*> unsafeFreezeRef v <*> unsafeFreezeRef w
unsafeFreezeImpl :: [Name] -> Dec
unsafeFreezeImpl refVars = FunD 'unsafeFreezeRef [
Clause [TupP (VarP <$> refVars)]
(NormalB . liftApplyAllE tupConE $
(VarE 'unsafeFreezeRef `AppE`) . VarE <$> refVars
)
[]
]
listRefTuple
:: Int
-> Q Dec
listRefTuple n = do
valVars <- replicateM n (newName "x")
-- instance (Ref s a ~ ra, Ref s b ~ rb) => ListRefTuple s (ra, rb) '[a, b] where
pure $ InstanceD
Nothing
(zipWith refConstr refVars tyVars)
(listRefTupleS (tuplerT (VarT <$> refVars)) `AppT`
(liftedList (VarT <$> tyVars))
)
[ tupToListImpl valVars
, listToTupImpl valVars
]
where
tuplerT :: [Type] -> Type
tuplerT = applyAllT (TupleT n)
tupConE :: Exp
tupConE = ConE (tupleDataName n)
listRefTupleS :: Type -> Type
listRefTupleS = ((ConT ''ListRefTuple `AppT` VarT (mkName "s")) `AppT`)
refS :: Type -> Type
refS = ((ConT ''Ref `AppT` VarT (mkName "s")) `AppT`)
tyVarsStr :: [String]
tyVarsStr = take n tyVarNames
tyVars :: [Name]
tyVars = mkName <$> tyVarsStr
refVars :: [Name]
refVars = mkName . ("r" ++) <$> tyVarsStr
refConstr :: Name -> Name -> Pred
refConstr r v = (EqualityT `AppT` refS (VarT v))
`AppT` VarT r
-- tupleToListRef (x, y) = x :> y :> Nil
tupToListImpl :: [Name] -> Dec
tupToListImpl valVars = FunD 'tupleToListRef [
Clause [TupP (VarP <$> valVars)]
( NormalB
. foldr (\x y -> (ConE '(:>) `AppE` VarE x) `AppE` y) (ConE 'Nil)
$ valVars
)
[]
]
-- listRefToTuple (x :> y :> _) = (x, y)
listToTupImpl :: [Name] -> Dec
listToTupImpl valVars = FunD 'listRefToTuple [
Clause [ foldr (\x y -> ConP '(:>) [VarP x, y]) (ConP 'Nil []) valVars
]
( NormalB . applyAllE tupConE $
VarE <$> valVars
)
[]
]
applyAllT
:: Type
-> [Type]
-> Type
applyAllT = foldl' (\t m -> t `AppT` m)
-- | liftApplyAllE f [x,y,z] = f <$> x <*> y <*> z
liftApplyAllE
:: Exp
-> [Exp]
-> Exp
liftApplyAllE = foldl' (\t m -> (VarE '(<*>) `AppE` t) `AppE` m)
. (VarE 'pure `AppE`)
-- | applyAllE f [x,y,z] = f x y z
applyAllE
:: Exp
-> [Exp]
-> Exp
applyAllE = foldl' (\t m -> t `AppE` m)
-- | sequenceAllE [x,y,z] = x *> y *> z
sequenceAllE
:: [Exp]
-> Exp
sequenceAllE = foldr1 (\x y -> (VarE '(*>) `AppE` x) `AppE` y)
liftedList
:: [Type]
-> Type
liftedList = foldr (\x y -> (PromotedConsT `AppT` x) `AppT` y) PromotedNilT