{- Data/Singletons/Single/Data.hs (c) Richard Eisenberg 2013 rae@cs.brynmawr.edu Singletonizes constructors. -} {-# LANGUAGE ParallelListComp, TupleSections, LambdaCase #-} module Data.Singletons.Single.Data where import Language.Haskell.TH.Desugar import Language.Haskell.TH.Desugar.OSet (OSet) import Language.Haskell.TH.Syntax import Data.Singletons.Single.Defun import Data.Singletons.Single.Monad import Data.Singletons.Single.Type import Data.Singletons.Single.Fixity import Data.Singletons.Promote.Type import Data.Singletons.Util import Data.Singletons.Names import Data.Singletons.Syntax import Control.Monad import Data.Foldable -- We wish to consider the promotion of "Rep" to be * -- not a promoted data constructor. singDataD :: DataDecl -> SgM [DDec] singDataD (DataDecl name tvbs ctors) = do let tvbNames = map extractTvbName tvbs k <- promoteType (foldType (DConT name) (map DVarT tvbNames)) ctors' <- mapM (singCtor name) ctors ctorFixities <- -- try to reify the fixity declarations for the constructors and then -- singletonize them. In case the reification fails, we default to an -- empty list of singletonized fixity declarations. -- why this works: -- 1. if we're in a call to 'genSingletons', the data type was defined -- earlier and its constructors are in scope, the reification succeeds. -- 2. if we're in a call to 'singletons', the reification will fail, but -- the fixity declaration will get singletonized by itself (not from -- here, look for other invocations of 'singInfixDecl') singFixityDeclarations [ n | DCon _ _ n _ _ <- ctors ] -- instance for SingKind fromSingClauses <- mapM mkFromSingClause ctors emptyFromSingClause <- mkEmptyFromSingClause toSingClauses <- mapM mkToSingClause ctors emptyToSingClause <- mkEmptyToSingClause let singKindInst = DInstanceD Nothing Nothing (map (singKindConstraint . DVarT) tvbNames) (DAppT (DConT singKindClassName) k) [ DTySynInstD $ DTySynEqn Nothing (DConT demoteName `DAppT` k) (foldType (DConT name) (map (DAppT demote . DVarT) tvbNames)) , DLetDec $ DFunD fromSingName (fromSingClauses `orIfEmpty` [emptyFromSingClause]) , DLetDec $ DFunD toSingName (toSingClauses `orIfEmpty` [emptyToSingClause]) ] let singDataName = singTyConName name -- e.g. type instance Sing @Nat = SNat singSynInst = DTySynInstD $ DTySynEqn Nothing (DConT singFamilyName `DAppKindT` k) (DConT singDataName) kindedSingTy = DForallT (map DPlainTV tvbNames) [] $ DArrowT `DAppT` k `DAppT` DConT typeKindName return $ (DDataD Data [] singDataName [] (Just kindedSingTy) ctors' []) : singSynInst : singKindInst : ctorFixities where -- in the Rep case, the names of the constructors are in the wrong scope -- (they're types, not datacons), so we have to reinterpret them. mkConName :: Name -> SgM Name mkConName | nameBase name == nameBase repName = mkDataName . nameBase | otherwise = return mkFromSingClause :: DCon -> SgM DClause mkFromSingClause c = do let (cname, numArgs) = extractNameArgs c cname' <- mkConName cname varNames <- replicateM numArgs (qNewName "b") return $ DClause [DConP (singDataConName cname) (map DVarP varNames)] (foldExp (DConE cname') (map (DAppE (DVarE fromSingName) . DVarE) varNames)) mkToSingClause :: DCon -> SgM DClause mkToSingClause (DCon _tvbs _cxt cname fields _rty) = do let types = tysOfConFields fields varNames <- mapM (const $ qNewName "b") types svarNames <- mapM (const $ qNewName "c") types promoted <- mapM promoteType types cname' <- mkConName cname let varPats = zipWith mkToSingVarPat varNames promoted recursiveCalls = zipWith mkRecursiveCall varNames promoted return $ DClause [DConP cname' varPats] (multiCase recursiveCalls (map (DConP someSingDataName . listify . DVarP) svarNames) (DAppE (DConE someSingDataName) (foldExp (DConE (singDataConName cname)) (map DVarE svarNames)))) mkToSingVarPat :: Name -> DKind -> DPat mkToSingVarPat varName ki = DSigP (DVarP varName) (DAppT (DConT demoteName) ki) mkRecursiveCall :: Name -> DKind -> DExp mkRecursiveCall var_name ki = DSigE (DAppE (DVarE toSingName) (DVarE var_name)) (DAppT (DConT someSingTypeName) ki) mkEmptyFromSingClause :: SgM DClause mkEmptyFromSingClause = do x <- qNewName "x" pure $ DClause [DVarP x] $ DCaseE (DVarE x) [] mkEmptyToSingClause :: SgM DClause mkEmptyToSingClause = do x <- qNewName "x" pure $ DClause [DVarP x] $ DConE someSingDataName `DAppE` DCaseE (DVarE x) [] -- refine a constructor. singCtor :: Name -> DCon -> SgM DCon -- polymorphic constructors are handled just -- like monomorphic ones -- the polymorphism in -- the kind is automatic singCtor dataName (DCon _tvbs cxt name fields rty) | not (null cxt) = fail "Singling of constrained constructors not yet supported" | otherwise = do let types = tysOfConFields fields sName = singDataConName name sCon = DConE sName pCon = DConT name indexNames <- mapM (const $ qNewName "n") types let indices = map DVarT indexNames kinds <- mapM promoteType types let bound_kvs = foldMap fvDType kinds args <- zipWithM (buildArgType bound_kvs) types indices rty' <- promoteType rty let tvbs = map DPlainTV (toList bound_kvs) ++ zipWith DKindedTV indexNames kinds kindedIndices = zipWith DSigT indices kinds -- SingI instance for data constructor emitDecs [DInstanceD Nothing Nothing (map (DAppT (DConT singIName)) indices) (DAppT (DConT singIName) (foldType pCon kindedIndices)) [DLetDec $ DValD (DVarP singMethName) (foldExp sCon (map (const $ DVarE singMethName) types))]] -- SingI instances for defunctionalization symbols. Note that we don't -- support contexts in constructors at the moment, so it's fine for now to -- just assume that the context is always (). emitDecs =<< singDefuns name DataName [] (map Just kinds) (Just rty') let noBang = Bang NoSourceUnpackedness NoSourceStrictness conFields = case fields of DNormalC dInfix _ -> DNormalC dInfix $ map (noBang,) args DRecC rec_fields -> DRecC [ (singValName field_name, noBang, arg) | (field_name, _, _) <- rec_fields | arg <- args ] return $ DCon tvbs [] sName conFields (DConT (singTyConName dataName) `DAppT` foldType pCon indices) where buildArgType :: OSet Name -> DType -> DType -> SgM DType buildArgType bound_kvs ty index = do (ty', _, _, _, _, _) <- singType bound_kvs index ty return ty'