module Data.Extensible.TH (mkField, decFields, decFieldsDeriving, decEffects) where
import Data.Proxy
import Data.Extensible.Internal
import Data.Extensible.Internal.Rig (Optic')
import Data.Extensible.Class (Extensible, itemAssoc)
import Data.Extensible.Effect
import Data.Extensible.Field
import Data.Extensible.Plain (accessing)
import Language.Haskell.TH
import Data.Char
import Data.Functor.Identity
import Control.Monad
#if !MIN_VERSION_base(4,8,0)
import Data.Foldable (foldMap)
#endif
mkField :: String -> DecsQ
mkField str = fmap concat $ forM (words str) $ \s@(x:xs) -> do
let st = litT (strTyLit s)
let name = mkName $ if isLower x then x : xs else '_' : x : xs
let lbl = conE 'Proxy `sigE` (conT ''Proxy `appT` st)
sequence [sigD name $ conT ''FieldOptic `appT` st
, valD (varP name) (normalB $ varE 'itemAssoc `appE` lbl) []
, return $ PragmaD $ InlineP name Inline FunLike AllPhases
]
decFields :: DecsQ -> DecsQ
decFields = decFieldsDeriving []
decFieldsDeriving :: [Name] -> DecsQ -> DecsQ
decFieldsDeriving drv' ds = ds >>= fmap concat . mapM mkBody
where
mkBody (NewtypeD cx name_ tvs (NormalC nc [(st, ty)]) drv) = do
let name = let (x:xs) = nameBase name_ in mkName $ toLower x : xs
xs_ = mkName "xs"
f_ = mkName "f"
p_ = mkName "p"
t_ = mkName "t"
ext = varT t_ `appT` conT ''Identity `appT` varT xs_
tvs' = PlainTV xs_ : PlainTV f_ : PlainTV p_ : PlainTV t_ : tvs
sequence [return $ NewtypeD cx name_ tvs (NormalC nc [(st, ty)]) (drv' ++ drv)
,sigD name
#if MIN_VERSION_template_haskell(2,10,0)
$ forallT tvs' (sequence [conT ''Member `appT` varT xs_ `appT` conT name_
, conT ''Extensible `appT` varT f_ `appT` varT p_ `appT` varT t_])
#else
$ forallT tvs' (sequence [classP ''Member [varT xs_, conT name_]
, classP ''Extensible [varT f_, varT p_, varT t_]])
#endif
$ conT ''Optic' `appT` varT p_ `appT` varT f_ `appT` ext `appT` return ty
, valD (varP name) (normalB $ varE 'accessing `appE` conE nc) []
, return $ PragmaD $ InlineP name Inline FunLike AllPhases
]
mkBody (TySynD name_ tvs ty) = mkBody (NewtypeD [] name_ tvs (NormalC (mkName (nameBase name_)) [(NotStrict, ty)]) [])
mkBody _ = fail "Unsupported declaration: genField handles newtype declarations or type synonyms"
decEffects :: DecsQ -> DecsQ
decEffects decs = decs >>= \ds -> fmap concat $ forM ds $ \case
DataD _ _ (fmap getTV -> tyvars) cs _
| not (null tyvars) -> fmap concat $ forM cs $ \case
NormalC con st -> mk tyvars [] con st
ForallC _ eqs (NormalC con st) -> mk tyvars eqs con st
p -> do
runIO (print p)
fail "Unsupported constructor"
_ -> fail "mkEffects accepts GADT declaration"
where
mk tyvars eqs con (fmap snd -> argTypes) = do
#if MIN_VERSION_template_haskell(2,10,0)
let dic_ = [(v, t) | AppT (AppT EqualityT (VarT v)) t <- eqs]
#else
let dic_ = [(v, t) | EqualP (VarT v) t <- eqs]
#endif
let dic = dic_ ++ [(t, VarT v) | (v, VarT t) <- dic_]
let tvs = map mkName $ concatMap (flip replicateM ['a'..'z']) [1..]
let params' = do
(t, v) <- zip tyvars tvs
case lookup t dic of
Just (VarT p) -> return (t, p)
_ -> return (t, v)
let (_, fts) = foldMap (\(p, t) -> maybe ([VarT t], [t]) (\case
VarT _ -> ([VarT t], [t])
x -> ([x], [])) (lookup p dic)) (init params')
let argTypes' = map (\case
VarT n -> maybe (VarT n) VarT $ lookup n params'
x -> x) argTypes
let (extra, result) = case lookup (last tyvars) dic of
Just (VarT v) -> (id, case lookup v params' of
Just p -> VarT p
Nothing -> VarT v)
Just t -> (id, t)
Nothing -> ((PlainTV (mkName "x"):), VarT $ mkName "x")
let rt = ConT ''Eff `AppT` VarT (mkName "xs") `AppT` result
let fun = foldr (\x y -> ArrowT `AppT` x `AppT` y) rt argTypes'
let eff = ConT ''Action
`AppT` foldr (\x y -> PromotedConsT `AppT` x `AppT` y) PromotedNilT argTypes'
`AppT` result
let nameT = LitT $ StrTyLit $ nameBase con
#if MIN_VERSION_template_haskell(2,10,0)
let cx = ConT ''Associate
`AppT` nameT
`AppT` eff
`AppT` VarT (mkName "xs")
#else
let cx = ClassP ''Associate [nameT, eff, VarT (mkName "xs")]
#endif
let typ = ForallT (PlainTV (mkName "xs") : extra (map PlainTV fts)) [cx] fun
let lifter = VarE 'liftEff `AppE` (ConE 'Proxy `SigE` AppT (ConT ''Proxy) nameT)
let argNames = map (mkName . ("a" ++) . show) [0..length argTypes1]
let ex = lifter
`AppE` foldr (\x y -> ConE 'AArgument `AppE` x `AppE` y)
(ConE 'AResult)
(map VarE argNames)
let fName = let (ch : rest) = nameBase con in mkName $ toLower ch : rest
return [SigD fName typ
, FunD fName [Clause (map VarP argNames) (NormalB ex) []]]
getTV (PlainTV n) = n
getTV (KindedTV n _) = n