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.
 
 
 

382 lines
12 KiB

{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Elab where
import Control.Monad.Reader
import Control.Exception
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
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.Pretty
import Syntax
import Data.Map (Map)
import Data.Text (Text)
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 0) dom $ \name ->
Lam p name <$> check body (rng (VVar name))
check tm (VPi P.Im dom (Closure var rng)) =
assume var dom $ \name ->
Lam P.Im name <$> check tm (rng (VVar name))
check (P.Lam p v b) ty = do
porp <- isPiType p =<< forceIO ty
case porp of
It'sProd d r wp ->
assume (Bound v 0) d $ \name ->
wp . Lam p name <$> check b (r (VVar name))
It'sPath li le ri wp -> do
tm <- assume (Bound v 0) VI $ \var ->
Lam P.Ex var <$> check b (force (li (VVar var)))
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 0) (VIsOne phi) $ \var ->
wp . Lam p var <$> check b a
It'sPartialP phi a wp ->
assume (Bound v 0) (VIsOne phi) $ \var ->
wp . Lam p var <$> check b (a @@ VVar var)
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 0) d_nf \var -> do
r <- check r ty
pure (Pi p var d r)
check (P.Sigma s d r) ty = do
isSort ty
d <- check d ty
d_nf <- eval d
assume (Bound s 0) d_nf \var -> do
r <- check r ty
pure (Sigma var d r)
check (P.Let items body) ty = do
checkLetItems mempty items \decs -> do
body <- check body ty
pure (Let decs body)
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 0) (VIsOne phi) $ \var -> do
env <- ask
for (truthAssignments phi (getEnv env)) $ \e -> do
let env' = env{ getEnv = e }
(Just var,) <$> check rhs (eval' env' dom_q)
Nothing -> do
env <- ask
for (truthAssignments phi (getEnv env)) $ \e -> do
let env' = env{ getEnv = e }
(Nothing,) <$> check rhs (eval' env' dom_q)
pure (n, (phi, 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 v (VIsOne formula) . const
Nothing -> id
k $ for_ eqns $ \(n', (formula', (binding, rhs'))) -> do
let
k = case binding of
Just v -> assume v (VIsOne formula) . const
Nothing -> id
truth = possible mempty (iand formula formula')
add [] = id
add ((~(HVar x), True):xs) = redefine x VI VI1 . add xs
add ((~(HVar x), False):xs) = redefine 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 name y)) eqns))))
check exp ty = do
(tm, has) <- switch $ infer exp
wp <- isConvertibleTo has ty
pure (wp tm)
checkLetItems :: Map Text (Maybe NFType) -> [P.LetItem] -> ([(Name, Term, Term)] -> ElabM a) -> ElabM a
checkLetItems _ [] cont = cont []
checkLetItems map (P.LetDecl v t:xs) cont = do
t <- check t VTypeω
t_nf <- eval t
assume (Defined v 0) t_nf \_ ->
checkLetItems (Map.insert v (Just t_nf) map) xs cont
checkLetItems map (P.LetBind name rhs:xs) cont = do
case Map.lookup name map of
Nothing -> do
(tm, ty) <- infer rhs
tm_nf <- eval tm
define (Defined name 0) ty tm_nf \name' ->
checkLetItems map xs \xs ->
cont ((name', quote ty, tm):xs)
Just Nothing -> throwElab $ Redefinition (Defined name 0)
Just (Just ty_nf) -> do
rhs <- check rhs ty_nf
rhs_nf <- eval rhs
define (Defined name 0) ty_nf rhs_nf \name' ->
checkLetItems (Map.insert name Nothing map) xs \xs ->
cont ((name', quote ty_nf, rhs):xs)
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 t = isSort (force t) where
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 x = isPiType p (force x) where
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 name dom $ \name -> 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 t = isSigmaType (force t) where
isSigmaType (VSigma d (Closure _ k)) = pure (d, k, id)
isSigmaType t = do
dom <- newMeta VType
name <- newName
assume name dom $ \name -> do
rng <- newMeta VType
wp <- isConvertibleTo t (VSigma dom (Closure name (const rng)))
pure (dom, const rng, wp)
isPartialType :: NFType -> ElabM (NFEndp, Value)
isPartialType t = isPartialType (force t) where
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 (flip Defined 0 <$> name) ty_nf (const k)
checkStatement (P.Postulate []) k = k
checkStatement (P.Postulate ((name, ty):xs)) k = do
ty <- check ty VTypeω
ty_nf <- eval ty
assume (Defined name 0) ty_nf \name ->
local (\e -> e { definedNames = Set.insert name (definedNames e) }) (checkStatement (P.Postulate xs) k)
checkStatement (P.Defn name rhs) k = do
ty <- asks (Map.lookup name . nameMap)
case ty of
Nothing -> do
(tm, ty) <- infer rhs
tm_nf <- eval tm
define (Defined name 0) ty tm_nf (const k)
Just nm -> do
ty_nf <- getNfType nm
t <- asks (Set.member nm . definedNames)
when t $ throwElab (Redefinition (Defined name 0))
rhs <- check rhs ty_nf
rhs_nf <- eval rhs
define (Defined name 0) ty_nf rhs_nf $ \name ->
local (\e -> e { definedNames = Set.insert name (definedNames e) }) 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 0) (wiType wi) (wiValue wi) $ \name ->
local (\e -> e { definedNames = Set.insert name (definedNames e) }) 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)