{-# LANGUAGE CPP #-}
module Composite.TH
( withProxies
, withLensesAndProxies
, withPrismsAndProxies
, withOpticsAndProxies
) where
import Composite.CoRecord (Field, fieldValPrism)
import Composite.Record ((:->), Record, rlens)
import Control.Lens (Prism', _1, _head, each, over, toListOf)
import Data.Char (toLower)
import Data.List (foldl')
import Data.Maybe (catMaybes)
import Data.Proxy (Proxy(Proxy))
import Data.Vinyl (RecApplicative)
import Data.Vinyl.Lens (type (∈))
import Language.Haskell.TH
( Q, newName, mkName, nameBase
, Body(NormalB), cxt, Dec(PragmaD, SigD, ValD), Exp(VarE), Inline(Inlinable), Name, Pat(VarP), Phases(AllPhases), Pragma(InlineP), RuleMatch(FunLike)
, Type(AppT, ConT, ForallT, VarT), TyVarBndr(PlainTV, KindedTV), varT
#if MIN_VERSION_template_haskell(2,17,0)
, Specificity(SpecifiedSpec)
#endif
)
import Language.Haskell.TH.Lens (_TySynD)
withProxies :: Q [Dec] -> Q [Dec]
withProxies :: Q [Dec] -> Q [Dec]
withProxies Q [Dec]
qDecs = do
[Dec]
decs <- Q [Dec]
qDecs
[[Dec]]
proxyDecs <- (Name -> Q [Dec]) -> [Name] -> Q [[Dec]]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse Name -> Q [Dec]
proxyDecForName (Getting (Endo [Name]) [Dec] Name -> [Dec] -> [Name]
forall a s. Getting (Endo [a]) s a -> s -> [a]
toListOf ((Dec -> Const (Endo [Name]) Dec)
-> [Dec] -> Const (Endo [Name]) [Dec]
forall s t a b. Each s t a b => Traversal s t a b
each ((Dec -> Const (Endo [Name]) Dec)
-> [Dec] -> Const (Endo [Name]) [Dec])
-> ((Name -> Const (Endo [Name]) Name)
-> Dec -> Const (Endo [Name]) Dec)
-> Getting (Endo [Name]) [Dec] Name
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Name, [TyVarBndrUnit], Type)
-> Const (Endo [Name]) (Name, [TyVarBndrUnit], Type))
-> Dec -> Const (Endo [Name]) Dec
Prism' Dec (Name, [TyVarBndrUnit], Type)
_TySynD (((Name, [TyVarBndrUnit], Type)
-> Const (Endo [Name]) (Name, [TyVarBndrUnit], Type))
-> Dec -> Const (Endo [Name]) Dec)
-> ((Name -> Const (Endo [Name]) Name)
-> (Name, [TyVarBndrUnit], Type)
-> Const (Endo [Name]) (Name, [TyVarBndrUnit], Type))
-> (Name -> Const (Endo [Name]) Name)
-> Dec
-> Const (Endo [Name]) Dec
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Name -> Const (Endo [Name]) Name)
-> (Name, [TyVarBndrUnit], Type)
-> Const (Endo [Name]) (Name, [TyVarBndrUnit], Type)
forall s t a b. Field1 s t a b => Lens s t a b
_1) [Dec]
decs)
[Dec] -> Q [Dec]
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Dec] -> Q [Dec]) -> [Dec] -> Q [Dec]
forall a b. (a -> b) -> a -> b
$ [Dec]
decs [Dec] -> [Dec] -> [Dec]
forall a. Semigroup a => a -> a -> a
<> [[Dec]] -> [Dec]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[Dec]]
proxyDecs
where
proxyDecForName :: Name -> Q [Dec]
proxyDecForName Name
tySynName = do
let tySynType :: Q Type
tySynType = Type -> Q Type
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Type -> Q Type) -> Type -> Q Type
forall a b. (a -> b) -> a -> b
$ Name -> Type
ConT Name
tySynName
proxyName :: Name
proxyName = String -> Name
mkName (String -> Name) -> (Name -> String) -> Name -> Name
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ASetter String String Char Char
-> (Char -> Char) -> String -> String
forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
over ASetter String String Char Char
forall s a. Cons s s a a => Traversal' s a
_head Char -> Char
toLower (String -> String) -> (Name -> String) -> Name -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Name -> String
nameBase (Name -> Name) -> Name -> Name
forall a b. (a -> b) -> a -> b
$ Name
tySynName
Type
proxyType <- [t|Proxy $tySynType|]
Exp
proxyVal <- [|Proxy|]
[Dec] -> Q [Dec]
forall (f :: * -> *) a. Applicative f => a -> f a
pure
[ Pragma -> Dec
PragmaD (Name -> Inline -> RuleMatch -> Phases -> Pragma
InlineP Name
proxyName Inline
Inlinable RuleMatch
FunLike Phases
AllPhases)
, Name -> Type -> Dec
SigD Name
proxyName Type
proxyType
, Pat -> Body -> [Dec] -> Dec
ValD (Name -> Pat
VarP Name
proxyName) (Exp -> Body
NormalB Exp
proxyVal) []
]
withLensesAndProxies :: Q [Dec] -> Q [Dec]
withLensesAndProxies :: Q [Dec] -> Q [Dec]
withLensesAndProxies = Bool -> Bool -> Q [Dec] -> Q [Dec]
withBoilerplate Bool
True Bool
False
withPrismsAndProxies :: Q [Dec] -> Q [Dec]
withPrismsAndProxies :: Q [Dec] -> Q [Dec]
withPrismsAndProxies = Bool -> Bool -> Q [Dec] -> Q [Dec]
withBoilerplate Bool
False Bool
True
withOpticsAndProxies :: Q [Dec] -> Q [Dec]
withOpticsAndProxies :: Q [Dec] -> Q [Dec]
withOpticsAndProxies = Bool -> Bool -> Q [Dec] -> Q [Dec]
withBoilerplate Bool
True Bool
True
#if MIN_VERSION_template_haskell(2,17,0)
tyUnitToSpec :: Specificity -> TyVarBndr () -> TyVarBndr Specificity
tyUnitToSpec x (PlainTV n ()) = PlainTV n x
tyUnitToSpec x (KindedTV n () k) = KindedTV n x k
fieldDecUnitToSpec :: Specificity -> FieldDec () -> FieldDec Specificity
fieldDecUnitToSpec x (FieldDec n b t v) = FieldDec n (map (tyUnitToSpec x) b) t v
data FieldDec a = FieldDec
#else
data FieldDec = FieldDec
#endif
{ FieldDec -> Name
fieldName :: Name
#if MIN_VERSION_template_haskell(2,17,0)
, fieldBinders :: [TyVarBndr a]
#else
, FieldDec -> [TyVarBndrUnit]
fieldBinders :: [TyVarBndr]
#endif
, FieldDec -> Type
fieldTypeApplied :: Type
, FieldDec -> Type
fieldValueType :: Type
}
withBoilerplate :: Bool -> Bool -> Q [Dec] -> Q [Dec]
withBoilerplate :: Bool -> Bool -> Q [Dec] -> Q [Dec]
withBoilerplate Bool
generateLenses Bool
generatePrisms Q [Dec]
qDecs = do
[Dec]
decs <- Q [Dec]
qDecs
let fieldDecs :: [FieldDec]
fieldDecs = [Maybe FieldDec] -> [FieldDec]
forall a. [Maybe a] -> [a]
catMaybes ([Maybe FieldDec] -> [FieldDec])
-> ([Dec] -> [Maybe FieldDec]) -> [Dec] -> [FieldDec]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Name, [TyVarBndrUnit], Type) -> Maybe FieldDec)
-> [(Name, [TyVarBndrUnit], Type)] -> [Maybe FieldDec]
forall a b. (a -> b) -> [a] -> [b]
map (Name, [TyVarBndrUnit], Type) -> Maybe FieldDec
fieldDecMay ([(Name, [TyVarBndrUnit], Type)] -> [Maybe FieldDec])
-> ([Dec] -> [(Name, [TyVarBndrUnit], Type)])
-> [Dec]
-> [Maybe FieldDec]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Getting
(Endo [(Name, [TyVarBndrUnit], Type)])
[Dec]
(Name, [TyVarBndrUnit], Type)
-> [Dec] -> [(Name, [TyVarBndrUnit], Type)]
forall a s. Getting (Endo [a]) s a -> s -> [a]
toListOf ((Dec -> Const (Endo [(Name, [TyVarBndrUnit], Type)]) Dec)
-> [Dec] -> Const (Endo [(Name, [TyVarBndrUnit], Type)]) [Dec]
forall s t a b. Each s t a b => Traversal s t a b
each ((Dec -> Const (Endo [(Name, [TyVarBndrUnit], Type)]) Dec)
-> [Dec] -> Const (Endo [(Name, [TyVarBndrUnit], Type)]) [Dec])
-> (((Name, [TyVarBndrUnit], Type)
-> Const
(Endo [(Name, [TyVarBndrUnit], Type)])
(Name, [TyVarBndrUnit], Type))
-> Dec -> Const (Endo [(Name, [TyVarBndrUnit], Type)]) Dec)
-> Getting
(Endo [(Name, [TyVarBndrUnit], Type)])
[Dec]
(Name, [TyVarBndrUnit], Type)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Name, [TyVarBndrUnit], Type)
-> Const
(Endo [(Name, [TyVarBndrUnit], Type)])
(Name, [TyVarBndrUnit], Type))
-> Dec -> Const (Endo [(Name, [TyVarBndrUnit], Type)]) Dec
Prism' Dec (Name, [TyVarBndrUnit], Type)
_TySynD) ([Dec] -> [FieldDec]) -> [Dec] -> [FieldDec]
forall a b. (a -> b) -> a -> b
$ [Dec]
decs
#if MIN_VERSION_template_haskell(2,17,0)
let sFieldDecs = map (fieldDecUnitToSpec SpecifiedSpec) fieldDecs
#endif
[[Dec]]
proxyDecs <- (FieldDec -> Q [Dec]) -> [FieldDec] -> Q [[Dec]]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse FieldDec -> Q [Dec]
proxyDecFor [FieldDec]
fieldDecs
#if MIN_VERSION_template_haskell(2,17,0)
lensDecs <- if generateLenses then traverse lensDecFor sFieldDecs else pure []
prismDecs <- if generatePrisms then traverse prismDecFor sFieldDecs else pure []
#else
[[Dec]]
lensDecs <- if Bool
generateLenses then (FieldDec -> Q [Dec]) -> [FieldDec] -> Q [[Dec]]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse FieldDec -> Q [Dec]
lensDecFor [FieldDec]
fieldDecs else [[Dec]] -> Q [[Dec]]
forall (f :: * -> *) a. Applicative f => a -> f a
pure []
[[Dec]]
prismDecs <- if Bool
generatePrisms then (FieldDec -> Q [Dec]) -> [FieldDec] -> Q [[Dec]]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse FieldDec -> Q [Dec]
prismDecFor [FieldDec]
fieldDecs else [[Dec]] -> Q [[Dec]]
forall (f :: * -> *) a. Applicative f => a -> f a
pure []
#endif
[Dec] -> Q [Dec]
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Dec] -> Q [Dec]) -> [Dec] -> Q [Dec]
forall a b. (a -> b) -> a -> b
$ [Dec]
decs [Dec] -> [Dec] -> [Dec]
forall a. Semigroup a => a -> a -> a
<> [[Dec]] -> [Dec]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[Dec]]
proxyDecs [Dec] -> [Dec] -> [Dec]
forall a. Semigroup a => a -> a -> a
<> [[Dec]] -> [Dec]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[Dec]]
lensDecs [Dec] -> [Dec] -> [Dec]
forall a. Semigroup a => a -> a -> a
<> [[Dec]] -> [Dec]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[Dec]]
prismDecs
#if MIN_VERSION_template_haskell(2,17,0)
fieldDecMay :: (Name, [TyVarBndr ()], Type) -> Maybe (FieldDec ())
#else
fieldDecMay :: (Name, [TyVarBndr], Type) -> Maybe FieldDec
#endif
fieldDecMay :: (Name, [TyVarBndrUnit], Type) -> Maybe FieldDec
fieldDecMay (Name
fieldName, [TyVarBndrUnit]
fieldBinders, Type
ty) = case Type
ty of
AppT (AppT (ConT Name
n) Type
_) Type
fieldValueType | Name
n Name -> Name -> Bool
forall a. Eq a => a -> a -> Bool
== ''(:->) ->
let fieldTypeApplied :: Type
fieldTypeApplied = (Type -> Type -> Type) -> Type -> [Type] -> Type
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' Type -> Type -> Type
AppT (Name -> Type
ConT Name
fieldName) ((TyVarBndrUnit -> Type) -> [TyVarBndrUnit] -> [Type]
forall a b. (a -> b) -> [a] -> [b]
map TyVarBndrUnit -> Type
binderTy [TyVarBndrUnit]
fieldBinders)
#if MIN_VERSION_template_haskell(2,17,0)
binderTy (PlainTV n' _ ) = VarT n'
binderTy (KindedTV n' _ _) = VarT n'
#else
binderTy :: TyVarBndrUnit -> Type
binderTy (PlainTV Name
n' ) = Name -> Type
VarT Name
n'
binderTy (KindedTV Name
n' Type
_) = Name -> Type
VarT Name
n'
#endif
in FieldDec -> Maybe FieldDec
forall a. a -> Maybe a
Just (FieldDec -> Maybe FieldDec) -> FieldDec -> Maybe FieldDec
forall a b. (a -> b) -> a -> b
$ FieldDec :: Name -> [TyVarBndrUnit] -> Type -> Type -> FieldDec
FieldDec {[TyVarBndrUnit]
Type
Name
fieldTypeApplied :: Type
fieldValueType :: Type
fieldBinders :: [TyVarBndrUnit]
fieldName :: Name
fieldValueType :: Type
fieldTypeApplied :: Type
fieldBinders :: [TyVarBndrUnit]
fieldName :: Name
..}
Type
_ ->
Maybe FieldDec
forall a. Maybe a
Nothing
lensNameFor, prismNameFor, proxyNameFor :: Name -> Name
lensNameFor :: Name -> Name
lensNameFor = String -> Name
mkName (String -> Name) -> (Name -> String) -> Name -> Name
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ASetter String String Char Char
-> (Char -> Char) -> String -> String
forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
over ASetter String String Char Char
forall s a. Cons s s a a => Traversal' s a
_head Char -> Char
toLower (String -> String) -> (Name -> String) -> Name -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Name -> String
nameBase
prismNameFor :: Name -> Name
prismNameFor = String -> Name
mkName (String -> Name) -> (Name -> String) -> Name -> Name
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String
"_" String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Name -> String) -> Name -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Name -> String
nameBase
proxyNameFor :: Name -> Name
proxyNameFor = String -> Name
mkName (String -> Name) -> (Name -> String) -> Name -> Name
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"_") (String -> String) -> (Name -> String) -> Name -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ASetter String String Char Char
-> (Char -> Char) -> String -> String
forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
over ASetter String String Char Char
forall s a. Cons s s a a => Traversal' s a
_head Char -> Char
toLower (String -> String) -> (Name -> String) -> Name -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Name -> String
nameBase
#if MIN_VERSION_template_haskell(2,17,0)
proxyDecFor :: FieldDec () -> Q [Dec]
#else
proxyDecFor :: FieldDec -> Q [Dec]
#endif
proxyDecFor :: FieldDec -> Q [Dec]
proxyDecFor (FieldDec { Name
fieldName :: Name
fieldName :: FieldDec -> Name
fieldName, Type
fieldTypeApplied :: Type
fieldTypeApplied :: FieldDec -> Type
fieldTypeApplied }) = do
let proxyName :: Name
proxyName = Name -> Name
proxyNameFor Name
fieldName
Type
proxyType <- [t|Proxy $(pure fieldTypeApplied)|]
Exp
proxyVal <- [|Proxy|]
[Dec] -> Q [Dec]
forall (f :: * -> *) a. Applicative f => a -> f a
pure
[ Pragma -> Dec
PragmaD (Name -> Inline -> RuleMatch -> Phases -> Pragma
InlineP Name
proxyName Inline
Inlinable RuleMatch
FunLike Phases
AllPhases)
, Name -> Type -> Dec
SigD Name
proxyName Type
proxyType
, Pat -> Body -> [Dec] -> Dec
ValD (Name -> Pat
VarP Name
proxyName) (Exp -> Body
NormalB Exp
proxyVal) []
]
#if MIN_VERSION_template_haskell(2,17,0)
lensDecFor :: FieldDec Specificity -> Q [Dec]
#else
lensDecFor :: FieldDec -> Q [Dec]
#endif
lensDecFor :: FieldDec -> Q [Dec]
lensDecFor (FieldDec {[TyVarBndrUnit]
Type
Name
fieldValueType :: Type
fieldTypeApplied :: Type
fieldBinders :: [TyVarBndrUnit]
fieldName :: Name
fieldValueType :: FieldDec -> Type
fieldTypeApplied :: FieldDec -> Type
fieldBinders :: FieldDec -> [TyVarBndrUnit]
fieldName :: FieldDec -> Name
..}) = do
Name
f <- String -> Q Name
newName String
"f"
Name
rs <- String -> Q Name
newName String
"rs"
let fTy :: Q Type
fTy = Name -> Q Type
varT Name
f
rsTy :: Q Type
rsTy = Name -> Q Type
varT Name
rs
proxyName :: Name
proxyName = Name -> Name
proxyNameFor Name
fieldName
lensName :: Name
lensName = Name -> Name
lensNameFor Name
fieldName
proxyVal :: Exp
proxyVal = Name -> Exp
VarE Name
proxyName
#if MIN_VERSION_template_haskell(2,17,0)
lensBinders = fieldBinders ++ [PlainTV f SpecifiedSpec, PlainTV rs SpecifiedSpec]
#else
lensBinders :: [TyVarBndrUnit]
lensBinders = [TyVarBndrUnit]
fieldBinders [TyVarBndrUnit] -> [TyVarBndrUnit] -> [TyVarBndrUnit]
forall a. [a] -> [a] -> [a]
++ [Name -> TyVarBndrUnit
PlainTV Name
f, Name -> TyVarBndrUnit
PlainTV Name
rs]
#endif
[Type]
lensContext <- [Q Type] -> CxtQ
cxt [ [t| Functor $fTy |], [t| $(pure fieldTypeApplied) ∈ $rsTy |] ]
Type
lensType <- [t| ($(pure fieldValueType) -> $fTy $(pure fieldValueType)) -> (Record $rsTy -> $fTy (Record $rsTy)) |]
Exp
rlensVal <- [| rlens $(pure proxyVal) |]
[Dec] -> Q [Dec]
forall (f :: * -> *) a. Applicative f => a -> f a
pure
[ Pragma -> Dec
PragmaD (Name -> Inline -> RuleMatch -> Phases -> Pragma
InlineP Name
lensName Inline
Inlinable RuleMatch
FunLike Phases
AllPhases)
, Name -> Type -> Dec
SigD Name
lensName ([TyVarBndrUnit] -> [Type] -> Type -> Type
ForallT [TyVarBndrUnit]
lensBinders [Type]
lensContext Type
lensType)
, Pat -> Body -> [Dec] -> Dec
ValD (Name -> Pat
VarP Name
lensName) (Exp -> Body
NormalB Exp
rlensVal) []
]
#if MIN_VERSION_template_haskell(2,17,0)
prismDecFor :: FieldDec Specificity -> Q [Dec]
#else
prismDecFor :: FieldDec -> Q [Dec]
#endif
prismDecFor :: FieldDec -> Q [Dec]
prismDecFor (FieldDec {[TyVarBndrUnit]
Type
Name
fieldValueType :: Type
fieldTypeApplied :: Type
fieldBinders :: [TyVarBndrUnit]
fieldName :: Name
fieldValueType :: FieldDec -> Type
fieldTypeApplied :: FieldDec -> Type
fieldBinders :: FieldDec -> [TyVarBndrUnit]
fieldName :: FieldDec -> Name
..}) = do
Name
rs <- String -> Q Name
newName String
"rs"
let rsTy :: Q Type
rsTy = Name -> Q Type
varT Name
rs
proxyName :: Name
proxyName = Name -> Name
proxyNameFor Name
fieldName
prismName :: Name
prismName = Name -> Name
prismNameFor Name
fieldName
proxyVal :: Exp
proxyVal = Name -> Exp
VarE Name
proxyName
#if MIN_VERSION_template_haskell(2,17,0)
prismBinders = fieldBinders ++ [PlainTV rs SpecifiedSpec]
#else
prismBinders :: [TyVarBndrUnit]
prismBinders = [TyVarBndrUnit]
fieldBinders [TyVarBndrUnit] -> [TyVarBndrUnit] -> [TyVarBndrUnit]
forall a. [a] -> [a] -> [a]
++ [Name -> TyVarBndrUnit
PlainTV Name
rs]
#endif
[Type]
prismContext <- [Q Type] -> CxtQ
cxt [ [t| RecApplicative $rsTy |], [t| $(pure fieldTypeApplied) ∈ $rsTy |] ]
Type
prismType <- [t| Prism' (Field $rsTy) $(pure fieldValueType) |]
Exp
fieldPrismVal <- [| fieldValPrism $(pure proxyVal) |]
[Dec] -> Q [Dec]
forall (f :: * -> *) a. Applicative f => a -> f a
pure
[ Pragma -> Dec
PragmaD (Name -> Inline -> RuleMatch -> Phases -> Pragma
InlineP Name
prismName Inline
Inlinable RuleMatch
FunLike Phases
AllPhases)
, Name -> Type -> Dec
SigD Name
prismName ([TyVarBndrUnit] -> [Type] -> Type -> Type
ForallT [TyVarBndrUnit]
prismBinders [Type]
prismContext Type
prismType)
, Pat -> Body -> [Dec] -> Dec
ValD (Name -> Pat
VarP Name
prismName) (Exp -> Body
NormalB Exp
fieldPrismVal) []
]