language-oberon-0.2.1: src/Transformation/Deep/TH.hs
-- | This module exports the templates for automatic instance deriving of "Transformation.Deep" type classes. The most
-- common way to use it would be
--
-- > import qualified Transformation.Deep.TH
-- > data MyDataType f' f = ...
-- > $(Transformation.Deep.TH.deriveFunctor ''MyDataType)
--
{-# Language TemplateHaskell #-}
-- Adapted from https://wiki.haskell.org/A_practical_Template_Haskell_Tutorial
module Transformation.Deep.TH (deriveAll, deriveFunctor, deriveFoldable, deriveDownTraversable, deriveUpTraversable)
where
import Control.Monad (replicateM)
import Data.Monoid ((<>))
import Language.Haskell.TH
import Language.Haskell.TH.Syntax (BangType, VarBangType, getQ, putQ)
import qualified Transformation
import qualified Transformation.Deep
import qualified Rank2.TH
data Deriving = Deriving { _constructor :: Name, _variableN :: Name, _variable1 :: Name }
deriveAll :: Name -> Q [Dec]
deriveAll ty = foldr f (pure []) [Rank2.TH.deriveFunctor, Rank2.TH.deriveFoldable, Rank2.TH.deriveTraversable,
Transformation.Deep.TH.deriveFunctor, Transformation.Deep.TH.deriveFoldable,
Transformation.Deep.TH.deriveDownTraversable,
Transformation.Deep.TH.deriveUpTraversable]
where f derive rest = (<>) <$> derive ty <*> rest
deriveFunctor :: Name -> Q [Dec]
deriveFunctor ty = do
t <- varT <$> newName "t"
p <- varT <$> newName "p"
q <- varT <$> newName "q"
let deepConstraint ty = conT ''Transformation.Deep.Functor `appT` t `appT` ty `appT` p `appT` q
shallowConstraint ty =
conT ''Transformation.Functor `appT` t `appT` p `appT` q `appT` (ty `appT` q `appT` q)
(instanceType, cs) <- reifyConstructors ty
(constraints, dec) <- genDeepmap deepConstraint shallowConstraint cs
sequence [instanceD (cxt (appT (conT ''Functor) p : map pure constraints))
(deepConstraint instanceType)
[pure dec]]
deriveFoldable :: Name -> Q [Dec]
deriveFoldable ty = do
t <- varT <$> newName "t"
f <- varT <$> newName "f"
m <- varT <$> newName "m"
let deepConstraint ty = conT ''Transformation.Deep.Foldable `appT` t `appT` ty `appT` f `appT` m
shallowConstraint ty =
conT ''Transformation.Foldable `appT` t `appT` f `appT` m `appT` (ty `appT` f `appT` f)
(instanceType, cs) <- reifyConstructors ty
(constraints, dec) <- genFoldMap deepConstraint shallowConstraint cs
sequence [instanceD (cxt (appT (conT ''Monoid) m : appT (conT ''Foldable) f : map pure constraints))
(deepConstraint instanceType)
[pure dec]]
deriveDownTraversable :: Name -> Q [Dec]
deriveDownTraversable ty = do
t <- varT <$> newName "t"
p <- varT <$> newName "p"
q <- varT <$> newName "q"
m <- varT <$> newName "m"
let deepConstraint ty = conT ''Transformation.Deep.DownTraversable `appT` t `appT` ty `appT` p `appT` q `appT` m
shallowConstraint ty =
conT ''Transformation.Traversable `appT` t `appT` p `appT` q `appT` m `appT` (ty `appT` p `appT` p)
(instanceType, cs) <- reifyConstructors ty
(constraints, dec) <- genTraverseDown deepConstraint shallowConstraint cs
sequence [instanceD (cxt (appT (conT ''Monad) m : appT (conT ''Traversable) q : map pure constraints))
(deepConstraint instanceType)
[pure dec]]
deriveUpTraversable :: Name -> Q [Dec]
deriveUpTraversable ty = do
t <- varT <$> newName "t"
p <- varT <$> newName "p"
q <- varT <$> newName "q"
m <- varT <$> newName "m"
let deepConstraint ty = conT ''Transformation.Deep.UpTraversable `appT` t `appT` ty `appT` p `appT` q `appT` m
shallowConstraint ty =
conT ''Transformation.Traversable `appT` t `appT` p `appT` q `appT` m `appT` (ty `appT` q `appT` q)
(instanceType, cs) <- reifyConstructors ty
(constraints, dec) <- genTraverseUp deepConstraint shallowConstraint cs
sequence [instanceD (cxt (appT (conT ''Monad) m : appT (conT ''Traversable) p : map pure constraints))
(deepConstraint instanceType)
[pure dec]]
reifyConstructors :: Name -> Q (TypeQ, [Con])
reifyConstructors ty = do
(TyConI tyCon) <- reify ty
(tyConName, tyVars, _kind, cs) <- case tyCon of
DataD _ nm tyVars kind cs _ -> return (nm, tyVars, kind, cs)
NewtypeD _ nm tyVars kind c _ -> return (nm, tyVars, kind, [c])
_ -> fail "deriveApply: tyCon may not be a type synonym."
let (KindedTV tyVar (AppT (AppT ArrowT StarT) StarT) :
KindedTV tyVar' (AppT (AppT ArrowT StarT) StarT) : _) = reverse tyVars
instanceType = foldl apply (conT tyConName) (reverse $ drop 2 $ reverse tyVars)
apply t (PlainTV name) = appT t (varT name)
apply t (KindedTV name _) = appT t (varT name)
putQ (Deriving tyConName tyVar' tyVar)
return (instanceType, cs)
genDeepmap :: (Q Type -> Q Type) -> (Q Type -> Q Type) -> [Con] -> Q ([Type], Dec)
genDeepmap deepConstraint shallowConstraint cs = do
(constraints, clauses) <- unzip <$> mapM (genDeepmapClause deepConstraint shallowConstraint) cs
return (concat constraints, FunD '(Transformation.Deep.<$>) clauses)
genFoldMap :: (Q Type -> Q Type) -> (Q Type -> Q Type) -> [Con] -> Q ([Type], Dec)
genFoldMap deepConstraint shallowConstraint cs = do
(constraints, clauses) <- unzip <$> mapM (genFoldMapClause deepConstraint shallowConstraint) cs
return (concat constraints, FunD 'Transformation.Deep.foldMap clauses)
genTraverseDown :: (Q Type -> Q Type) -> (Q Type -> Q Type) -> [Con] -> Q ([Type], Dec)
genTraverseDown deepConstraint shallowConstraint cs = do
(constraints, clauses) <- unzip <$> mapM (genTraverseClause genTraverseDownField deepConstraint shallowConstraint) cs
return (concat constraints, FunD 'Transformation.Deep.traverseDown clauses)
genTraverseUp :: (Q Type -> Q Type) -> (Q Type -> Q Type) -> [Con] -> Q ([Type], Dec)
genTraverseUp deepConstraint shallowConstraint cs = do
(constraints, clauses) <- unzip <$> mapM (genTraverseClause genTraverseUpField deepConstraint shallowConstraint) cs
return (concat constraints, FunD 'Transformation.Deep.traverseUp clauses)
genDeepmapClause :: (Q Type -> Q Type) -> (Q Type -> Q Type) -> Con -> Q ([Type], Clause)
genDeepmapClause deepConstraint shallowConstraint (NormalC name fieldTypes) = do
t <- newName "t"
fieldNames <- replicateM (length fieldTypes) (newName "x")
let pats = [varP t, parensP (conP name $ map varP fieldNames)]
constraintsAndFields = zipWith newField fieldNames fieldTypes
newFields = map (snd <$>) constraintsAndFields
body = normalB $ appsE $ conE name : newFields
newField :: Name -> BangType -> Q ([Type], Exp)
newField x (_, fieldType) = genDeepmapField (varE t) fieldType deepConstraint shallowConstraint (varE x) id
constraints <- (concat . (fst <$>)) <$> sequence constraintsAndFields
(,) constraints <$> clause pats body []
genDeepmapClause deepConstraint shallowConstraint (RecC name fields) = do
f <- newName "f"
x <- newName "x"
let body = normalB $ recConE name $ (snd <$>) <$> constraintsAndFields
constraintsAndFields = map newNamedField fields
newNamedField :: VarBangType -> Q ([Type], (Name, Exp))
newNamedField (fieldName, _, fieldType) =
((,) fieldName <$>)
<$> genDeepmapField (varE f) fieldType deepConstraint shallowConstraint (appE (varE fieldName) (varE x)) id
constraints <- (concat . (fst <$>)) <$> sequence constraintsAndFields
(,) constraints <$> clause [varP f, varP x] body []
genFoldMapClause :: (Q Type -> Q Type) -> (Q Type -> Q Type) -> Con -> Q ([Type], Clause)
genFoldMapClause deepConstraint shallowConstraint (NormalC name fieldTypes) = do
t <- newName "t"
fieldNames <- replicateM (length fieldTypes) (newName "x")
let pats = [varP t, parensP (conP name $ map varP fieldNames)]
constraintsAndFields = zipWith newField fieldNames fieldTypes
newFields = map (snd <$>) constraintsAndFields
body | null newFields = [| mempty |]
| otherwise = foldr1 append newFields
append a b = [| $(a) <> $(b) |]
newField :: Name -> BangType -> Q ([Type], Exp)
newField x (_, fieldType) = genFoldMapField (varE t) fieldType deepConstraint shallowConstraint (varE x) id
constraints <- (concat . (fst <$>)) <$> sequence constraintsAndFields
(,) constraints <$> clause pats (normalB body) []
genFoldMapClause deepConstraint shallowConstraint (RecC _name fields) = do
t <- newName "t"
x <- newName "x"
let body | null fields = [| mempty |]
| otherwise = foldr1 append $ (snd <$>) <$> constraintsAndFields
append a b = [| $(a) <> $(b) |]
constraintsAndFields = map newNamedField fields
newNamedField :: VarBangType -> Q ([Type], Exp)
newNamedField (fieldName, _, fieldType) =
genFoldMapField (varE t) fieldType deepConstraint shallowConstraint (appE (varE fieldName) (varE x)) id
constraints <- (concat . (fst <$>)) <$> sequence constraintsAndFields
(,) constraints <$> clause [varP t, varP x] (normalB body) []
type GenTraverseFieldType = Q Exp -> Type -> (Q Type -> Q Type) -> (Q Type -> Q Type) -> Q Exp -> (Q Exp -> Q Exp)
-> Q ([Type], Exp)
genTraverseClause :: GenTraverseFieldType -> (Q Type -> Q Type) -> (Q Type -> Q Type) -> Con -> Q ([Type], Clause)
genTraverseClause genTraverseField deepConstraint shallowConstraint (NormalC name fieldTypes) = do
t <- newName "t"
fieldNames <- replicateM (length fieldTypes) (newName "x")
let pats = [varP t, parensP (conP name $ map varP fieldNames)]
constraintsAndFields = zipWith newField fieldNames fieldTypes
newFields = map (snd <$>) constraintsAndFields
body | null fieldTypes = [| pure $(conE name) |]
| otherwise = fst $ foldl apply (conE name, False) newFields
apply (a, False) b = ([| $(a) <$> $(b) |], True)
apply (a, True) b = ([| $(a) <*> $(b) |], True)
newField :: Name -> BangType -> Q ([Type], Exp)
newField x (_, fieldType) = genTraverseField (varE t) fieldType deepConstraint shallowConstraint (varE x) id
constraints <- (concat . (fst <$>)) <$> sequence constraintsAndFields
(,) constraints <$> clause pats (normalB body) []
genTraverseClause genTraverseField deepConstraint shallowConstraint (RecC name fields) = do
f <- newName "f"
x <- newName "x"
let constraintsAndFields = map newNamedField fields
body | null fields = [| pure $(conE name) |]
| otherwise = fst (foldl apply (conE name, False) $ map (snd . snd <$>) constraintsAndFields)
apply (a, False) b = ([| $(a) <$> $(b) |], True)
apply (a, True) b = ([| $(a) <*> $(b) |], True)
newNamedField :: VarBangType -> Q ([Type], (Name, Exp))
newNamedField (fieldName, _, fieldType) =
((,) fieldName <$>)
<$> genTraverseField (varE f) fieldType deepConstraint shallowConstraint (appE (varE fieldName) (varE x)) id
constraints <- (concat . (fst <$>)) <$> sequence constraintsAndFields
(,) constraints <$> clause [varP f, varP x] (normalB body) []
genDeepmapField :: Q Exp -> Type -> (Q Type -> Q Type) -> (Q Type -> Q Type) -> Q Exp -> (Q Exp -> Q Exp)
-> Q ([Type], Exp)
genDeepmapField trans fieldType deepConstraint shallowConstraint fieldAccess wrap = do
Just (Deriving _ typeVarN typeVar1) <- getQ
case fieldType of
AppT ty (AppT (AppT con v1) v2) | ty == VarT typeVar1, v1 == VarT typeVarN, v2 == VarT typeVarN ->
(,) <$> ((:) <$> deepConstraint (pure con) <*> ((:[]) <$> shallowConstraint (pure con)))
<*> appE (wrap [| (Transformation.fmap $trans . (Transformation.Deep.fmap $trans <$>)) |]) fieldAccess
AppT ty _ | ty == VarT typeVar1 ->
(,) [] <$> (wrap (varE 'Transformation.fmap `appE` trans) `appE` fieldAccess)
AppT (AppT con v1) v2 | v1 == VarT typeVarN, v2 == VarT typeVar1 ->
(,) <$> ((:[]) <$> deepConstraint (pure con))
<*> appE (wrap [| Transformation.Deep.fmap $trans |]) fieldAccess
AppT t1 t2 | t1 /= VarT typeVar1 ->
genDeepmapField trans t2 deepConstraint shallowConstraint fieldAccess (wrap . appE (varE '(<$>)))
SigT ty _kind -> genDeepmapField trans ty deepConstraint shallowConstraint fieldAccess wrap
ParensT ty -> genDeepmapField trans ty deepConstraint shallowConstraint fieldAccess wrap
_ -> (,) [] <$> fieldAccess
genFoldMapField :: Q Exp -> Type -> (Q Type -> Q Type) -> (Q Type -> Q Type) -> Q Exp -> (Q Exp -> Q Exp)
-> Q ([Type], Exp)
genFoldMapField trans fieldType deepConstraint shallowConstraint fieldAccess wrap = do
Just (Deriving _ typeVarN typeVar1) <- getQ
case fieldType of
AppT ty (AppT (AppT con v1) v2) | ty == VarT typeVar1, v1 == VarT typeVarN, v2 == VarT typeVarN ->
(,) <$> ((:) <$> deepConstraint (pure con) <*> ((:[]) <$> shallowConstraint (pure con)))
<*> appE (wrap [| foldMap (Transformation.Deep.foldMap $trans) |]) fieldAccess
AppT ty _ | ty == VarT typeVar1 ->
(,) [] <$> (wrap (varE 'Transformation.foldMap `appE` trans) `appE` fieldAccess)
AppT (AppT con v1) v2 | v1 == VarT typeVarN, v2 == VarT typeVar1 ->
(,) <$> ((:[]) <$> deepConstraint (pure con))
<*> appE (wrap [| Transformation.Deep.foldMap $trans |]) fieldAccess
AppT t1 t2 | t1 /= VarT typeVar1 ->
genFoldMapField trans t2 deepConstraint shallowConstraint fieldAccess (wrap . appE (varE 'foldMap))
SigT ty _kind -> genFoldMapField trans ty deepConstraint shallowConstraint fieldAccess wrap
ParensT ty -> genFoldMapField trans ty deepConstraint shallowConstraint fieldAccess wrap
_ -> (,) [] <$> [| mempty |]
genTraverseDownField :: GenTraverseFieldType
genTraverseDownField trans fieldType deepConstraint shallowConstraint fieldAccess wrap = do
Just (Deriving _ typeVarN typeVar1) <- getQ
case fieldType of
AppT ty (AppT (AppT con v1) v2) | ty == VarT typeVar1, v1 == VarT typeVarN, v2 == VarT typeVarN ->
(,) <$> ((:) <$> deepConstraint (pure con) <*> ((:[]) <$> shallowConstraint (pure con)))
<*> appE (wrap [| (>>= traverse (Transformation.Deep.traverseDown $trans)) . Transformation.traverse $trans |])
fieldAccess
AppT ty _ | ty == VarT typeVar1 ->
(,) [] <$> (wrap (varE 'Transformation.traverse `appE` trans) `appE` fieldAccess)
AppT (AppT con v1) v2 | v1 == VarT typeVarN, v2 == VarT typeVar1 ->
(,) <$> ((:[]) <$> deepConstraint (pure con))
<*> appE (wrap [| Transformation.Deep.traverseDown $trans |]) fieldAccess
AppT t1 t2 | t1 /= VarT typeVar1 ->
genTraverseDownField trans t2 deepConstraint shallowConstraint fieldAccess (wrap . appE (varE 'traverse))
SigT ty _kind -> genTraverseDownField trans ty deepConstraint shallowConstraint fieldAccess wrap
ParensT ty -> genTraverseDownField trans ty deepConstraint shallowConstraint fieldAccess wrap
_ -> (,) [] <$> [| pure $fieldAccess |]
genTraverseUpField :: GenTraverseFieldType
genTraverseUpField trans fieldType deepConstraint shallowConstraint fieldAccess wrap = do
Just (Deriving _ typeVarN typeVar1) <- getQ
case fieldType of
AppT ty (AppT (AppT con v1) v2) | ty == VarT typeVar1, v1 == VarT typeVarN, v2 == VarT typeVarN ->
(,) <$> ((:) <$> deepConstraint (pure con) <*> ((:[]) <$> shallowConstraint (pure con)))
<*> appE (wrap [| (>>= Transformation.traverse $trans) . traverse (Transformation.Deep.traverseUp $trans) |])
fieldAccess
AppT ty _ | ty == VarT typeVar1 ->
(,) [] <$> (wrap (varE 'Transformation.traverse `appE` trans) `appE` fieldAccess)
AppT (AppT con v1) v2 | v1 == VarT typeVarN, v2 == VarT typeVar1 ->
(,) <$> ((:[]) <$> deepConstraint (pure con))
<*> appE (wrap [| Transformation.Deep.traverseUp $trans |]) fieldAccess
AppT t1 t2 | t1 /= VarT typeVar1 ->
genTraverseUpField trans t2 deepConstraint shallowConstraint fieldAccess (wrap . appE (varE 'traverse))
SigT ty _kind -> genTraverseUpField trans ty deepConstraint shallowConstraint fieldAccess wrap
ParensT ty -> genTraverseUpField trans ty deepConstraint shallowConstraint fieldAccess wrap
_ -> (,) [] <$> [| pure $fieldAccess |]