module Data.Binding.Hobbits.Internal.Closed where
import Language.Haskell.TH (Q, Exp(..), Type(..))
import qualified Language.Haskell.TH as TH
import qualified Language.Haskell.TH.ExpandSyns as TH
import qualified Data.Generics as SYB
import qualified Language.Haskell.TH.Syntax as TH
newtype Closed a = Closed { unClosed :: a }
#if MIN_VERSION_template_haskell(2,11,0)
reifyNameType :: TH.Name -> Q Type
reifyNameType n =
TH.reify n >>= \i ->
case i of
TH.VarI _ ty _ -> return ty
_ -> fail $ "hobbits Panic -- could not reify `" ++ show n ++ "'."
#else
reifyNameType :: TH.Name -> Q Type
reifyNameType n =
TH.reify n >>= \i ->
case i of
TH.VarI _ ty _ _ -> return ty
_ -> fail $ "hobbits Panic -- could not reify `" ++ show n ++ "'."
#endif
mkClosed :: Q Exp -> Q Exp
mkClosed e = AppE (ConE 'Closed) `fmap` e >>= SYB.everywhereM (SYB.mkM w) where
w e@(VarE n@(TH.Name _ flav)) = case flav of
TH.NameG {} -> return e
TH.NameU {} -> return e
TH.NameL {} -> closed n >> return e
_ -> fail $ "`mkClosed' does not allow dynamically bound names: `"
++ show n ++ "'."
w e = return e
closed n = do
ty <- reifyNameType n
TH.expandSyns ty >>= w ty
where
w _ (AppT (ConT m) _) | m == ''Closed = return ()
w top_ty (ForallT _ _ ty') = w top_ty ty'
w top_ty _ =
fail $ "`mkClosed` requires non-global variables to have type `Closed'.\n\t`"
++ show (TH.ppr n) ++ "' does not. It's type is:\n\t `"
++ show (TH.ppr top_ty) ++ "'."