module Data.Singletons.Single where
import Prelude hiding ( exp )
import Language.Haskell.TH hiding ( cxt )
import Language.Haskell.TH.Syntax (Quasi(..))
import Data.Singletons.Deriving.Ord
import Data.Singletons.Deriving.Bounded
import Data.Singletons.Deriving.Enum
import Data.Singletons.Util
import Data.Singletons.Promote
import Data.Singletons.Promote.Monad ( promoteM )
import Data.Singletons.Promote.Type
import Data.Singletons.Names
import Data.Singletons.Single.Monad
import Data.Singletons.Single.Type
import Data.Singletons.Single.Data
import Data.Singletons.Single.Eq
import Data.Singletons.Syntax
import Data.Singletons.Partition
import Language.Haskell.TH.Desugar
import qualified Data.Map.Strict as Map
import Data.Map.Strict ( Map )
import Data.Maybe
import Control.Monad
import Data.List
genSingletons :: DsMonad q => [Name] -> q [Dec]
genSingletons names = do
checkForRep names
ddecs <- concatMapM (singInfo <=< dsInfo <=< reifyWithWarning) names
return $ decsToTH ddecs
singletons :: DsMonad q => q [Dec] -> q [Dec]
singletons qdecs = do
decs <- qdecs
singDecs <- wrapDesugar singTopLevelDecs decs
return (decs ++ singDecs)
singletonsOnly :: DsMonad q => q [Dec] -> q [Dec]
singletonsOnly = (>>= wrapDesugar singTopLevelDecs)
singEqInstances :: DsMonad q => [Name] -> q [Dec]
singEqInstances = concatMapM singEqInstance
singEqInstance :: DsMonad q => Name -> q [Dec]
singEqInstance name = do
promotion <- promoteEqInstance name
dec <- singEqualityInstance sEqClassDesc name
return $ dec ++ promotion
singEqInstancesOnly :: DsMonad q => [Name] -> q [Dec]
singEqInstancesOnly = concatMapM singEqInstanceOnly
singEqInstanceOnly :: DsMonad q => Name -> q [Dec]
singEqInstanceOnly name = singEqualityInstance sEqClassDesc name
singDecideInstances :: DsMonad q => [Name] -> q [Dec]
singDecideInstances = concatMapM singDecideInstance
singDecideInstance :: DsMonad q => Name -> q [Dec]
singDecideInstance name = singEqualityInstance sDecideClassDesc name
singEqualityInstance :: DsMonad q => EqualityClassDesc q -> Name -> q [Dec]
singEqualityInstance desc@(_, className, _) name = do
(tvbs, cons) <- getDataD ("I cannot make an instance of " ++
show className ++ " for it.") name
dtvbs <- mapM dsTvb tvbs
dcons <- concatMapM dsCon cons
let tyvars = map (DVarT . extractTvbName) dtvbs
kind = foldType (DConT name) tyvars
aName <- qNewName "a"
let aVar = DVarT aName
(scons, _) <- singM [] $ mapM (singCtor aVar) dcons
eqInstance <- mkEqualityInstance kind scons desc
return $ decToTH eqInstance
singOrdInstances :: DsMonad q => [Name] -> q [Dec]
singOrdInstances = concatMapM singOrdInstance
singOrdInstance :: DsMonad q => Name -> q [Dec]
singOrdInstance = singInstance mkOrdInstance "Ord"
singBoundedInstances :: DsMonad q => [Name] -> q [Dec]
singBoundedInstances = concatMapM singBoundedInstance
singBoundedInstance :: DsMonad q => Name -> q [Dec]
singBoundedInstance = singInstance mkBoundedInstance "Bounded"
singEnumInstances :: DsMonad q => [Name] -> q [Dec]
singEnumInstances = concatMapM singEnumInstance
singEnumInstance :: DsMonad q => Name -> q [Dec]
singEnumInstance = singInstance mkEnumInstance "Enum"
singInstance :: DsMonad q
=> (DType -> [DCon] -> q UInstDecl)
-> String -> Name -> q [Dec]
singInstance mk_inst inst_name name = do
(tvbs, cons) <- getDataD ("I cannot make an instance of " ++ inst_name
++ " for it.") name
dtvbs <- mapM dsTvb tvbs
dcons <- concatMapM dsCon cons
raw_inst <- mk_inst (foldType (DConT name) (map tvbToType dtvbs)) dcons
(a_inst, decs) <- promoteM [] $
promoteInstanceDec Map.empty raw_inst
decs' <- singDecsM [] $ (:[]) <$> singInstD a_inst
return $ decsToTH (decs ++ decs')
singInfo :: DsMonad q => DInfo -> q [DDec]
singInfo (DTyConI dec _) =
singTopLevelDecs [] [dec]
singInfo (DPrimTyConI _name _numArgs _unlifted) =
fail "Singling of primitive type constructors not supported"
singInfo (DVarI _name _ty _mdec) =
fail "Singling of value info not supported"
singInfo (DTyVarI _name _ty) =
fail "Singling of type variable info not supported"
singTopLevelDecs :: DsMonad q => [Dec] -> [DDec] -> q [DDec]
singTopLevelDecs locals raw_decls = do
decls <- withLocalDeclarations locals $ expand raw_decls
PDecs { pd_let_decs = letDecls
, pd_class_decs = classes
, pd_instance_decs = insts
, pd_data_decs = datas } <- partitionDecs decls
((letDecEnv, classes', insts'), promDecls) <- promoteM locals $ do
promoteDataDecs datas
(_, letDecEnv) <- promoteLetDecs noPrefix letDecls
classes' <- mapM promoteClassDec classes
let meth_sigs = foldMap (lde_types . cd_lde) classes
insts' <- mapM (promoteInstanceDec meth_sigs) insts
return (letDecEnv, classes', insts')
singDecsM locals $ do
let letBinds = concatMap buildDataLets datas
++ concatMap buildMethLets classes
(newLetDecls, newDecls) <- bindLets letBinds $
singLetDecEnv letDecEnv $ do
newDataDecls <- concatMapM singDataD datas
newClassDecls <- mapM singClassD classes'
newInstDecls <- mapM singInstD insts'
return (newDataDecls ++ newClassDecls ++ newInstDecls)
return $ promDecls ++ (map DLetDec newLetDecls) ++ newDecls
buildDataLets :: DataDecl -> [(Name, DExp)]
buildDataLets (DataDecl _nd _name _tvbs cons _derivings) =
concatMap con_num_args cons
where
con_num_args :: DCon -> [(Name, DExp)]
con_num_args (DCon _tvbs _cxt name fields _rty) =
(name, wrapSingFun (length (tysOfConFields fields))
(promoteValRhs name) (DConE $ singDataConName name))
: rec_selectors fields
rec_selectors :: DConFields -> [(Name, DExp)]
rec_selectors (DNormalC {}) = []
rec_selectors (DRecC fields) =
let names = map fstOf3 fields in
[ (name, wrapSingFun 1 (promoteValRhs name) (DVarE $ singValName name))
| name <- names ]
buildMethLets :: UClassDecl -> [(Name, DExp)]
buildMethLets (ClassDecl { cd_lde = LetDecEnv { lde_types = meth_sigs } }) =
map mk_bind (Map.toList meth_sigs)
where
mk_bind (meth_name, meth_ty) =
( meth_name
, wrapSingFun (countArgs meth_ty) (promoteValRhs meth_name)
(DVarE $ singValName meth_name) )
singClassD :: AClassDecl -> SgM DDec
singClassD (ClassDecl { cd_cxt = cls_cxt
, cd_name = cls_name
, cd_tvbs = cls_tvbs
, cd_fds = cls_fundeps
, cd_lde = LetDecEnv { lde_defns = default_defns
, lde_types = meth_sigs
, lde_infix = fixities
, lde_proms = promoted_defaults } }) = do
(sing_sigs, _, tyvar_names, res_kis)
<- unzip4 <$> zipWithM (singTySig no_meth_defns meth_sigs)
meth_names (map promoteValRhs meth_names)
let default_sigs = catMaybes $ zipWith mk_default_sig meth_names sing_sigs
res_ki_map = Map.fromList (zip meth_names
(map (fromMaybe always_sig) res_kis))
sing_meths <- mapM (uncurry (singLetDecRHS (Map.fromList tyvar_names)
res_ki_map))
(Map.toList default_defns)
let fixities' = map (uncurry singInfixDecl) fixities
cls_cxt' <- mapM singPred cls_cxt
(kproxies, kproxy_pred) <- mkKProxies (map extractTvbName cls_tvbs)
return $ DClassD (cls_cxt' ++ kproxy_pred)
(singClassName cls_name) kproxies
cls_fundeps
(map DLetDec (sing_sigs ++ sing_meths ++ fixities') ++ default_sigs)
where
no_meth_defns = error "Internal error: can't find declared method type"
always_sig = error "Internal error: no signature for default method"
meth_names = Map.keys meth_sigs
mk_default_sig meth_name (DSigD s_name sty) =
DDefaultSigD s_name <$> add_constraints meth_name sty
mk_default_sig _ _ = error "Internal error: a singled signature isn't a signature."
add_constraints meth_name sty = do
prom_dflt <- Map.lookup meth_name promoted_defaults
let default_pred = foldl DAppPr (DConPr equalityName)
[ foldApply (promoteValRhs meth_name) tvs
, foldApply prom_dflt tvs ]
return $ DForallT tvbs (default_pred : cxt) (ravel args res)
where
(tvbs, cxt, args, res) = unravel sty
tvs = map tvbToType tvbs
singInstD :: AInstDecl -> SgM DDec
singInstD (InstDecl { id_cxt = cxt, id_name = inst_name
, id_arg_tys = inst_tys, id_meths = ann_meths }) = do
cxt' <- mapM singPred cxt
inst_kis <- mapM promoteType inst_tys
meths <- concatMapM (uncurry sing_meth) ann_meths
return (DInstanceD Nothing
cxt'
(foldl DAppT (DConT s_inst_name) (map kindParam inst_kis))
meths)
where
s_inst_name = singClassName inst_name
sing_meth :: Name -> ALetDecRHS -> SgM [DDec]
sing_meth name rhs = do
mb_s_info <- dsReify (singValName name)
(s_ty, tyvar_names, m_res_ki) <- case mb_s_info of
Just (DVarI _ (DForallT cls_kproxy_tvbs _cls_pred s_ty) _) -> do
#if __GLASGOW_HASKELL__ >= 711
let class_kvs = [ class_kv | DKindedTV class_kv DStarT <- cls_kproxy_tvbs ]
#else
let class_kvs = map extract_kv cls_kproxy_tvbs
extract_kv (DKindedTV _kproxyVar (DConT _kproxyTy `DAppT` DVarT kv)) = kv
extract_kv k = error $ "sing_meth cannot extract a kind variable" ++
"\n" ++ show k ++
"\n" ++ show name ++
"\n" ++ show (singValName name) ++
"\n" ++ show mb_s_info
#endif
(sing_tvbs, _pred, _args, res_ty) = unravel s_ty
inst_kis <- mapM promoteType inst_tys
let subst = Map.fromList (zip class_kvs inst_kis)
m_res_ki = case res_ty of
_sing `DAppT` (_prom_func `DSigT` res_ki) -> Just (substKind subst res_ki)
_ -> Nothing
return (substType subst s_ty, map extractTvbName sing_tvbs, m_res_ki)
_ -> do
mb_info <- dsReify name
case mb_info of
Just (DVarI _ (DForallT cls_tvbs _cls_pred inner_ty) _) -> do
let subst = Map.fromList (zip (map extractTvbName cls_tvbs)
inst_tys)
(s_ty, _num_args, tyvar_names, res_ki) <- singType (promoteValRhs name)
(substType subst inner_ty)
return (s_ty, tyvar_names, Just res_ki)
_ -> fail $ "Cannot find type of method " ++ show name
let kind_map = maybe Map.empty (Map.singleton name) m_res_ki
meth' <- singLetDecRHS (Map.singleton name tyvar_names)
kind_map name rhs
return $ map DLetDec [DSigD (singValName name) s_ty, meth']
singLetDecEnv :: ALetDecEnv -> SgM a -> SgM ([DLetDec], a)
singLetDecEnv (LetDecEnv { lde_defns = defns
, lde_types = types
, lde_infix = infix_decls
, lde_proms = proms })
thing_inside = do
let prom_list = Map.toList proms
(typeSigs, letBinds, tyvarNames, res_kis)
<- unzip4 <$> mapM (uncurry (singTySig defns types)) prom_list
let infix_decls' = map (uncurry singInfixDecl) infix_decls
res_ki_map = Map.fromList [ (name, res_ki) | ((name, _), Just res_ki)
<- zip prom_list res_kis ]
bindLets letBinds $ do
let_decs <- mapM (uncurry (singLetDecRHS (Map.fromList tyvarNames) res_ki_map))
(Map.toList defns)
thing <- thing_inside
return (infix_decls' ++ typeSigs ++ let_decs, thing)
singInfixDecl :: Fixity -> Name -> DLetDec
singInfixDecl fixity name
| isUpcase name =
DInfixD fixity (singDataConName name)
| otherwise = DInfixD fixity (singValName name)
singTySig :: Map Name ALetDecRHS
-> Map Name DType
-> Name -> DType
-> SgM ( DLetDec
, (Name, DExp)
, (Name, [Name])
, Maybe DKind
)
singTySig defns types name prom_ty =
let sName = singValName name in
case Map.lookup name types of
Nothing -> do
num_args <- guess_num_args
(sty, tyvar_names) <- mk_sing_ty num_args
return ( DSigD sName sty
, (name, wrapSingFun num_args prom_ty (DVarE sName))
, (name, tyvar_names)
, Nothing )
Just ty -> do
(sty, num_args, tyvar_names, res_ki) <- singType prom_ty ty
return ( DSigD sName sty
, (name, wrapSingFun num_args prom_ty (DVarE sName))
, (name, tyvar_names)
, Just res_ki )
where
guess_num_args :: SgM Int
guess_num_args =
case Map.lookup name defns of
Nothing -> fail "Internal error: promotion known for something not let-bound."
Just (AValue _ n _) -> return n
Just (AFunction _ n _) -> return n
mk_sing_ty :: Int -> SgM (DType, [Name])
mk_sing_ty n = do
arg_names <- replicateM n (qNewName "arg")
return ( DForallT (map DPlainTV arg_names) []
(ravel (map (\nm -> singFamily `DAppT` DVarT nm) arg_names)
(singFamily `DAppT`
(foldl apply prom_ty (map DVarT arg_names))))
, arg_names )
singLetDecRHS :: Map Name [Name]
-> Map Name DKind
-> Name -> ALetDecRHS -> SgM DLetDec
singLetDecRHS _bound_names res_kis name (AValue prom num_arrows exp) =
DValD (DVarPa (singValName name)) <$>
(wrapUnSingFun num_arrows prom <$> singExp exp (Map.lookup name res_kis))
singLetDecRHS bound_names res_kis name (AFunction prom_fun num_arrows clauses) =
let tyvar_names = case Map.lookup name bound_names of
Nothing -> []
Just ns -> ns
res_ki = Map.lookup name res_kis
in
DFunD (singValName name) <$>
mapM (singClause prom_fun num_arrows tyvar_names res_ki) clauses
singClause :: DType
-> Int
-> [Name]
-> Maybe DKind
-> ADClause -> SgM DClause
singClause prom_fun num_arrows bound_names res_ki
(ADClause var_proms pats exp) = do
(sPats, prom_pats)
<- mapAndUnzipM (singPat (Map.fromList var_proms) Parameter) pats
let bound_name_tys = map DVarT bound_names
equalities = zip bound_name_tys prom_pats
applied_ty = foldl apply prom_fun bound_name_tys `maybeSigT` res_ki
sBody <- bindTyVarsEq var_proms applied_ty equalities $ singExp exp res_ki
let pattern_bound_names = zipWith const bound_names pats
sBody' = wrapUnSingFun (num_arrows length pats)
(foldl apply prom_fun (map DVarT pattern_bound_names)) sBody
return $ DClause sPats sBody'
data PatternContext = LetBinding
| CaseStatement
| Parameter
deriving Eq
checkIfBrainWillExplode :: Monad m => PatternContext -> m ()
checkIfBrainWillExplode CaseStatement = return ()
checkIfBrainWillExplode Parameter = return ()
checkIfBrainWillExplode _ =
fail $ "Can't use a singleton pattern outside of a case-statement or\n" ++
"do expression: GHC's brain will explode if you try. (Do try it!)"
singPat :: Map Name Name
-> PatternContext
-> DPat
-> SgM (DPat, DType)
singPat _var_proms _patCxt (DLitPa _lit) =
fail "Singling of literal patterns not yet supported"
singPat var_proms _patCxt (DVarPa name) = do
tyname <- case Map.lookup name var_proms of
Nothing ->
fail "Internal error: unknown variable when singling pattern"
Just tyname -> return tyname
return (DVarPa (singValName name), DVarT tyname)
singPat var_proms patCxt (DConPa name pats) = do
checkIfBrainWillExplode patCxt
(pats', tys) <- mapAndUnzipM (singPat var_proms patCxt) pats
return ( DConPa (singDataConName name) pats'
, foldl apply (promoteValRhs name) tys )
singPat var_proms patCxt (DTildePa pat) = do
qReportWarning
"Lazy pattern converted into regular pattern during singleton generation."
singPat var_proms patCxt pat
singPat var_proms patCxt (DBangPa pat) = do
(pat', ty) <- singPat var_proms patCxt pat
return (DBangPa pat', ty)
singPat _var_proms _patCxt DWildPa =
fail "Internal error: wildcard seen during singleton generation"
singExp :: ADExp -> Maybe DKind
-> SgM DExp
singExp (ADVarE err `ADAppE` arg) _res_ki
| err == errorName = DAppE (DVarE (singValName err)) <$>
singExp arg (Just (DConT symbolName))
singExp (ADVarE name) _res_ki = lookupVarE name
singExp (ADConE name) _res_ki = lookupConE name
singExp (ADLitE lit) _res_ki = singLit lit
singExp (ADAppE e1 e2) _res_ki = do
e1' <- singExp e1 Nothing
e2' <- singExp e2 Nothing
if isException e1'
then return e1'
else return $ (DVarE applySingName) `DAppE` e1' `DAppE` e2'
singExp (ADLamE var_proms prom_lam names exp) _res_ki = do
let sNames = map singValName names
exp' <- bindTyVars var_proms (foldl apply prom_lam (map (DVarT . snd) var_proms)) $
singExp exp Nothing
return $ wrapSingFun (length names) prom_lam $ DLamE sNames exp'
singExp (ADCaseE exp prom_exp matches ret_ty) res_ki =
DSigE <$> (DCaseE <$> singExp exp Nothing <*> mapM (singMatch prom_exp res_ki) matches)
<*> pure (singFamily `DAppT` (ret_ty `maybeSigT` res_ki))
singExp (ADLetE env exp) res_ki =
uncurry DLetE <$> singLetDecEnv env (singExp exp res_ki)
singExp (ADSigE {}) _ =
fail "Singling of explicit type annotations not yet supported."
isException :: DExp -> Bool
isException (DVarE n) = n == undefinedName
isException (DConE {}) = False
isException (DLitE {}) = False
isException (DAppE (DVarE fun) _) | nameBase fun == "sError" = True
isException (DAppE fun _) = isException fun
isException (DLamE _ _) = False
isException (DCaseE e _) = isException e
isException (DLetE _ e) = isException e
isException (DSigE e _) = isException e
isException (DStaticE e) = isException e
singMatch :: DType
-> Maybe DKind
-> ADMatch -> SgM DMatch
singMatch prom_scrut res_ki (ADMatch var_proms prom_match pat exp) = do
(sPat, prom_pat)
<- singPat (Map.fromList var_proms) CaseStatement pat
let equality
| DVarPa _ <- pat
, (ADVarE err) `ADAppE` _ <- exp
, err == errorName
= []
| otherwise = [(prom_pat, prom_scrut)]
sExp <- bindTyVarsEq var_proms (prom_match `DAppT` prom_pat `maybeSigT` res_ki) equality $
singExp exp res_ki
return $ DMatch sPat sExp
singLit :: Lit -> SgM DExp
singLit (IntegerL n)
| n >= 0 = return $
DVarE sFromIntegerName `DAppE`
(DVarE singMethName `DSigE`
(singFamily `DAppT` DLitT (NumTyLit n)))
| otherwise = do sLit <- singLit (IntegerL (n))
return $ DVarE sNegateName `DAppE` sLit
singLit lit = do
prom_lit <- promoteLitExp lit
return $ DVarE singMethName `DSigE` (singFamily `DAppT` prom_lit)
maybeSigT :: DType -> Maybe DKind -> DType
maybeSigT ty Nothing = ty
maybeSigT ty (Just ki) = ty `DSigT` ki