less prototype, less bad code implementation of CCHM type theory
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

337 lines
9.9 KiB

{-# LANGUAGE TupleSections #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Elab where
import Control.Monad.Reader
import Control.Exception
import qualified Data.Map.Strict as Map
import Data.Traversable
import Data.Typeable
import Data.Foldable
import Elab.Eval.Formula (possible, truthAssignments)
import Elab.WiredIn
import Elab.Monad
import Elab.Eval
import qualified Presyntax.Presyntax as P
import Prettyprinter
import Syntax
import Syntax.Pretty
import qualified Data.Text as T
infer :: P.Expr -> ElabM (Term, NFType)
infer (P.Span ex a b) = withSpan a b $ infer ex
infer (P.Var t) = do
name <- getNameFor t
nft <- getNfType name
pure (Ref name, nft)
infer (P.App p f x) = do
(f, f_ty) <- infer f
porp <- isPiType p f_ty
case porp of
It'sProd d r w -> do
x <- check x d
x_nf <- eval x
pure (App p (w f) x, r x_nf)
It'sPath li le ri wp -> do
x <- check x VI
x_nf <- eval x
pure (IElim (quote (fun li)) (quote le) (quote ri) (wp f) x, li x_nf)
It'sPartial phi a w -> do
x <- check x (VIsOne phi)
pure (App P.Ex (w f) x, a)
It'sPartialP phi a w -> do
x <- check x (VIsOne phi)
x_nf <- eval x
pure (App P.Ex (w f) x, a @@ x_nf)
infer (P.Proj1 x) = do
(tm, ty) <- infer x
(d, _, wp) <- isSigmaType ty
pure (Proj1 (wp tm), d)
infer (P.Proj2 x) = do
(tm, ty) <- infer x
tm_nf <- eval tm
(_, r, wp) <- isSigmaType ty
pure (Proj2 (wp tm), r (vProj1 tm_nf))
infer exp = do
t <- newMeta VType
tm <- switch $ check exp t
pure (tm, t)
check :: P.Expr -> NFType -> ElabM Term
check (P.Span ex a b) ty = withSpan a b $ check ex ty
check (P.Lam p var body) (VPi p' dom (Closure _ rng)) | p == p' =
assume (Bound var) dom $
Lam p var <$> check body (rng (VVar (Bound var)))
check tm (VPi P.Im dom (Closure var rng)) =
assume (Bound var) dom $
Lam P.Im var <$> check tm (rng (VVar (Bound var)))
check (P.Lam p v b) ty = do
porp <- isPiType p =<< forceIO ty
case porp of
It'sProd d r wp ->
assume (Bound v) d $
wp . Lam p v <$> check b (r (VVar (Bound v)))
It'sPath li le ri wp -> do
tm <- assume (Bound v) VI $
Lam P.Ex v <$> check b (force (li (VVar (Bound v))))
tm_nf <- eval tm
unify (tm_nf @@ VI0) le
`catchElab` (throwElab . WhenCheckingEndpoint le ri VI0)
unify (tm_nf @@ VI1) ri
`catchElab` (throwElab . WhenCheckingEndpoint le ri VI1)
pure (wp (PathIntro (quote (fun li)) (quote le) (quote ri) tm))
It'sPartial phi a wp ->
assume (Bound v) (VIsOne phi) $
wp . Lam p v <$> check b a
It'sPartialP phi a wp ->
assume (Bound v) (VIsOne phi) $
wp . Lam p v <$> check b (a @@ VVar (Bound v))
check (P.Pair a b) ty = do
(d, r, wp) <- isSigmaType =<< forceIO ty
a <- check a d
a_nf <- eval a
b <- check b (r a_nf)
pure (wp (Pair a b))
check (P.Pi p s d r) ty = do
isSort ty
d <- check d ty
d_nf <- eval d
assume (Bound s) d_nf $ do
r <- check r ty
pure (Pi p s d r)
check (P.Sigma s d r) ty = do
isSort ty
d <- check d ty
d_nf <- eval d
assume (Bound s) d_nf $ do
r <- check r ty
pure (Sigma s d r)
check (P.LamSystem bs) ty = do
(extent, dom) <- isPartialType ty
let dom_q = quote dom
eqns <- for (zip [(0 :: Int)..] bs) $ \(n, (formula, rhs)) -> do
phi <- checkFormula (P.condF formula)
rhses <-
case P.condV formula of
Just t -> assume (Bound t) (VIsOne phi) $ do
env <- ask
for (truthAssignments phi (getEnv env)) $ \e -> do
let env' = env{ getEnv = e }
check rhs (eval' env' dom_q)
Nothing -> do
env <- ask
for (truthAssignments phi (getEnv env)) $ \e -> do
let env' = env{ getEnv = e }
check rhs (eval' env' dom_q)
pure (n, (phi, (P.condV formula, head rhses)))
unify extent (foldl ior VI0 (map (fst . snd) eqns))
for_ eqns $ \(n, (formula, (binding, rhs))) -> do
let
k = case binding of
Just v -> assume (Bound v) (VIsOne formula)
Nothing -> id
k $ for_ eqns $ \(n', (formula', (binding, rhs'))) -> do
let
k = case binding of
Just v -> assume (Bound v) (VIsOne formula)
Nothing -> id
truth = possible mempty (iand formula formula')
add [] = id
add ((~(HVar x), True):xs) = define x VI VI1 . add xs
add ((~(HVar x), False):xs) = define x VI VI0 . add xs
k $ when ((n /= n') && fst truth) . add (Map.toList (snd truth)) $ do
vl <- eval rhs
vl' <- eval rhs'
unify vl vl'
`withNote` vsep [ pretty "These two cases must agree because they are both possible:"
, indent 2 $ pretty '*' <+> prettyTm (quote formula) <+> operator (pretty "=>") <+> prettyTm rhs
, indent 2 $ pretty '*' <+> prettyTm (quote formula') <+> operator (pretty "=>") <+> prettyTm rhs'
]
`withNote` (pretty "Consider this face, where both are true:" <+> showFace (snd truth))
name <- newName
let
mkB name (Just v, b) = App P.Ex (Lam P.Ex v b) (Ref name)
mkB _ (Nothing, b) = b
pure (Lam P.Ex name (System (Map.fromList (map (\(_, (x, y)) -> (quote x, mkB (Bound name) y)) eqns))))
check exp ty = do
(tm, has) <- switch $ infer exp
wp <- isConvertibleTo has ty
pure (wp tm)
checkFormula :: P.Formula -> ElabM Value
checkFormula P.FTop = pure VI1
checkFormula P.FBot = pure VI0
checkFormula (P.FAnd x y) = iand <$> checkFormula x <*> checkFormula y
checkFormula (P.FOr x y) = ior <$> checkFormula x <*> checkFormula y
checkFormula (P.FIs0 x) = do
nm <- getNameFor x
ty <- getNfType nm
unify ty VI
pure (inot (VVar nm))
checkFormula (P.FIs1 x) = do
nm <- getNameFor x
ty <- getNfType nm
unify ty VI
pure (VVar nm)
isSort :: NFType -> ElabM ()
isSort VType = pure ()
isSort VTypeω = pure ()
isSort vt@(VNe HMeta{} _) = unify vt VType
isSort vt = throwElab $ NotEqual vt VType
data ProdOrPath
= It'sProd { prodDmn :: NFType
, prodRng :: NFType -> NFType
, prodWrap :: Term -> Term
}
| It'sPath { pathLine :: NFType -> NFType
, pathLeft :: Value
, pathRight :: Value
, pathWrap :: Term -> Term
}
| It'sPartial { partialExtent :: NFEndp
, partialDomain :: Value
, partialWrap :: Term -> Term
}
| It'sPartialP { partialExtent :: NFEndp
, partialDomain :: Value
, partialWrap :: Term -> Term
}
isPiType :: P.Plicity -> NFType -> ElabM ProdOrPath
isPiType p (VPi p' d (Closure _ k)) | p == p' = pure (It'sProd d k id)
isPiType P.Ex (VPath li le ri) = pure (It'sPath (li @@) le ri id)
isPiType P.Ex (VPartial phi a) = pure (It'sPartial phi a id)
isPiType P.Ex (VPartialP phi a) = pure (It'sPartialP phi a id)
isPiType P.Ex (VPi P.Im d (Closure _ k)) = do
meta <- newMeta d
porp <- isPiType P.Ex (k meta)
pure $ case porp of
It'sProd d r w -> It'sProd d r (\f -> w (App P.Im f (quote meta)))
It'sPath l x y w -> It'sPath l x y (\f -> w (App P.Im f (quote meta)))
It'sPartial phi a w -> It'sPartial phi a (\f -> w (App P.Im f (quote meta)))
It'sPartialP phi a w -> It'sPartialP phi a (\f -> w (App P.Im f (quote meta)))
isPiType p t = do
dom <- newMeta VType
name <- newName
assume (Bound name) dom $ do
rng <- newMeta VType
wp <- isConvertibleTo t (VPi p dom (Closure name (const rng)))
pure (It'sProd dom (const rng) wp)
isSigmaType :: NFType -> ElabM (Value, NFType -> NFType, Term -> Term)
isSigmaType (VSigma d (Closure _ k)) = pure (d, k, id)
isSigmaType t = do
dom <- newMeta VType
name <- newName
assume (Bound name) dom $ do
rng <- newMeta VType
wp <- isConvertibleTo t (VSigma dom (Closure name (const rng)))
pure (dom, const rng, wp)
isPartialType :: NFType -> ElabM (NFEndp, Value)
isPartialType (VPartial phi a) = pure (phi, a)
isPartialType (VPartialP phi a) = pure (phi, a)
isPartialType t = do
phi <- newMeta VI
dom <- newMeta (VPartial phi VType)
unify t (VPartial phi dom)
pure (phi, dom)
checkStatement :: P.Statement -> ElabM a -> ElabM a
checkStatement (P.SpanSt s a b) k = withSpan a b $ checkStatement s k
checkStatement (P.Decl name ty) k = do
ty <- check ty VTypeω
ty_nf <- eval ty
assumes (Defined <$> name) ty_nf k
checkStatement (P.Defn name rhs) k = do
ty <- asks (Map.lookup (Defined name) . getEnv)
case ty of
Nothing -> do
(tm, ty) <- infer rhs
tm_nf <- eval tm
define (Defined name) ty tm_nf k
Just (ty_nf, nm) -> do
case nm of
VVar (Defined n') | n' == name -> pure ()
_ -> throwElab $ Redefinition (Defined name)
rhs <- check rhs ty_nf
rhs_nf <- eval rhs
define (Defined name) ty_nf rhs_nf k
checkStatement (P.Builtin winame var) k = do
wi <-
case Map.lookup winame wiredInNames of
Just wi -> pure wi
_ -> throwElab $ NoSuchPrimitive winame
let
check = do
nm <- getNameFor var
ty <- getNfType nm
unify ty (wiType wi)
`withNote` hsep [ pretty "Previous definition of", pretty nm, pretty "here" ]
`seeAlso` nm
env <- ask
liftIO $
runElab check env `catch` \(_ :: NotInScope) -> pure ()
define (Defined var) (wiType wi) (wiValue wi) k
checkStatement (P.ReplNf e) k = do
(e, _) <- infer e
e_nf <- eval e
h <- asks commHook
liftIO (h e_nf)
k
checkStatement (P.ReplTy e) k = do
(_, ty) <- infer e
h <- asks commHook
liftIO (h ty)
k
checkProgram :: [P.Statement] -> ElabM a -> ElabM a
checkProgram [] k = k
checkProgram (st:sts) k = checkStatement st $ checkProgram sts k
newtype Redefinition = Redefinition { getRedefName :: Name }
deriving (Show, Typeable, Exception)
data WhenCheckingEndpoint = WhenCheckingEndpoint { leftEndp :: Value, rightEndp :: Value, whichIsWrong :: NFEndp, exc :: SomeException }
deriving (Show, Typeable, Exception)