closed-classes-0.1: src/Data/Class/Closed/TH.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UnicodeSyntax #-}
{-# LANGUAGE ViewPatterns #-}
module Data.Class.Closed.TH where
import Data.Class.Closed (Closed)
import Data.Maybe (fromMaybe)
import Data.Proxy (Proxy (..))
import Language.Haskell.TH (Dec (..), Name, Q, TyVarBndr (..), Type (..),
appT, classD, clause, conT, defaultSigD,
funD, instanceWithOverlapD, nameBase,
newName, normalB, sigD, varT)
import Language.Haskell.TH.Syntax (getQ, putQ)
-- | Closes all declared classes. Any instances must be given in the same quote.
close ∷ Q [Dec] -> Q [Dec]
close = (>>= mapM modify) where
modify ∷ Dec → Q Dec
modify (ClassD context name tvs deps decs) = do
let con = conT name
ty = foldl (\t b → appT t (binderT b)) con tvs
private ← getPrivateName name
classD (pure context) name tvs deps -- class ... ⇒ C ... where
$ defaultSigD private [t|Closed $con ⇒ Proxy $ty|] -- default private ∷ Closed C ⇒ Proxy (C ...)
: sigD private [t|Proxy $ty|] -- private ∷ Proxy (C ...)
: funD private [clause [] (normalB [|Proxy|]) []] -- private = Proxy
: map pure decs -- ...
modify (InstanceD overlap context ty@(conName → Just name) decs) = do
private ← getPrivateName name
instanceWithOverlapD overlap (pure context) (pure ty) -- instance ... where
$ funD private [clause [] (normalB [|Proxy|]) []] -- private = Proxy
: map pure decs -- ...
modify dec = pure dec
#if MIN_VERSION_template_haskell(2, 17, 0)
binderT ∷ TyVarBndr flag → Q Type
binderT (PlainTV name _) = varT name
binderT (KindedTV name _ _) = varT name
#else
binderT ∷ TyVarBndr → Q Type
binderT (PlainTV name) = varT name
binderT (KindedTV name _) = varT name
#endif
getPrivateName ∷ Name → Q Name
getPrivateName name = do
cache ← fromMaybe [] <$> getQ
case lookup name cache of
Just private → return private
Nothing → do
temp ← newName ("_" ++ nameBase name)
private ← newName (show temp)
putQ ((name, private):cache)
return private
conName ∷ Type → Maybe Name
conName (ConT n) = Just n
conName (AppT x _) = conName x
conName (AppKindT x _) = conName x
conName (SigT x _) = conName x
conName (InfixT _ n _) = Just n
conName (UInfixT _ n _) = Just n
conName (ParensT x) = conName x
conName _ = Nothing