Prototype, extremely bad code implementation of CCHM Cubical 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.
 

390 lines
12 KiB

{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE DeriveAnyClass #-}
module Elab where
import Control.Exception
import Control.Monad
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import Data.Traversable
import Data.Set (Set)
import Data.Typeable
import Data.Foldable
import Eval
import qualified Presyntax as P
import Syntax
import Systems
data TypeError
= NotInScope String
| UnifyError UnifyError
| WrongFaces Value [([Value], Value, Elab.TypeError)]
| InSpan (Int, Int) (Int, Int) Elab.TypeError
| IncompleteSystem P.Formula P.Formula
| IncompatibleFaces (P.Formula, Term) (P.Formula, Term) Elab.TypeError
| InvalidSystem (Set Face) (Set Face)
deriving (Show, Typeable, Exception)
check :: Env -> P.Exp -> Value -> IO Term
check env (P.Span s e exp) wants =
check env exp wants
`catch` \case
InSpan s e err -> throwIO $ InSpan s e err
err -> throwIO $ InSpan s e err
`catch` \er -> throwIO $ InSpan s e (UnifyError er)
check env exp (VSub a fm@(toFormula -> Just phi) el) = do
tm <- check env exp a
for (addFormula phi env) \env -> do
let tm' = eval env tm
unifyTC env tm' el
pure (InclSub (quote a) (quote fm) (quote el) tm)
check env (P.Let v t d b) expected = do
ty <- check env t VTypeω
let ty' = eval env ty
d <- check env d ty'
let d' = eval env d
b <- check env { names = Map.insert v (ty', d') (names env) } b expected
pure (Let v (quote ty') d b)
check env (P.Pair a b) expected = do
(_, fst, snd) <- isSigmaType expected
a <- check env a fst
let a' = eval env a
b <- check env b (snd a')
pure (Pair a b)
check env (P.Partial fs) ty = do
let formula = orFormula (map fst fs)
(extent, ty) <- isPartialType formula ty
let ourFaces = Systems.faces formula
extentFaces = Systems.faces extent
unless (toDNF formula == toDNF extent) $
throwIO $ IncompleteSystem formula extent
let range = formulaToTm $ toDNF formula
rangeTm <- check env range VI
let rangeTy = eval env rangeTm
ts <- for fs $ \(fn, tn) -> do
tms <- for (addFormula fn env) \env -> check env tn ty
pure (fn, head tms)
fmap (System . FMap . Map.fromList) $ for ts \(fn, tn) -> do
for ts \(fm, tm) -> do
when (fn /= fm && possible (fn `P.And` fm)) do
for_ (addFormula (fn `P.And` fm) env) $ \env ->
unifyTC (env) (eval env tn) (eval env tm)
`catch` \e -> throwIO (IncompatibleFaces (fn, tn) (fm, tm) e)
pure (fn, tn)
check env exp (VPartial fm@(toFormula -> Just phi) a) = do
case addFormula phi env of
[] -> pure $ System (FMap mempty)
(x:xs) -> do
tm <- check x exp a
for_ xs $ \e -> check e exp a
pure tm
check env (P.Lam s b) expected = do
expc <- isPiOrPathType expected
case expc of
Left (_, d, r) -> do -- function
bd <- check env { names = Map.insert s (makeValueGluingSub d s) (names env) } b (r (VVar s))
pure (Lam s (quote d) bd)
Right (a, x, y) -> do
bd <- check env { names = Map.insert s (VI, VVar s) (names env) } b (a @@ VVar s)
let t = Lam s I bd
t' = eval env t
checkBoundary env [s] t'
[ ([VI0], x)
, ([VI1], y)
]
pure (PathI (quote a) (quote x) (quote y) s bd)
check env exp expected = do
(term, actual) <- infer env exp
unifyTC env actual expected
pure term
makeValueGluingSub :: Value -> String -> (Value, Value)
makeValueGluingSub ty@(VSub a phi a0) s = (ty, VOfSub a phi a0 (VVar s))
makeValueGluingSub ty s = (ty, VVar s)
addFormula :: P.Formula -> Env -> [Env]
addFormula (P.And x y) = addFormula x >=> addFormula y
addFormula (P.Or x y) = (++) <$> addFormula x <*> addFormula y
addFormula P.Top = pure
addFormula P.Bot = const []
addFormula (P.Is0 x) = \env -> pure env{ names = Map.insert x (VI, VI0) (names env) }
addFormula (P.Is1 x) = \env -> pure env{ names = Map.insert x (VI, VI1) (names env) }
unifyTC :: Env -> Value -> Value -> IO ()
unifyTC env a b = unify env a b `catch` \e -> const (throwIO (UnifyError (Mismatch a b))) (e :: UnifyError)
checkBoundary :: Env -> [String] -> Value -> [([Value], Value)] -> IO ()
checkBoundary env ns f = finish <=< go where
go :: [([Value], Value)] -> IO [([Value], Value, Elab.TypeError)]
go [] = pure []
go ((ixs, vl):faces) = do
let env' = foldr (\(x, t) env -> env { names = Map.insert x t (names env) }) env (zip ns (zip (repeat VI) ixs))
t <- try $ unifyTC env' (foldl (@@) f ixs) vl
case t of
Right _ -> go faces
Left e -> ((ixs, vl, e):) <$> go faces
finish [] = pure ()
finish xs = throwIO $ WrongFaces f xs
infer :: Env -> P.Exp -> IO (Term, Value)
infer env (P.Span s e exp) =
infer env exp
`catch` \case
InSpan s e err -> throwIO $ InSpan s e err
err -> throwIO $ InSpan s e err
`catch` \er -> throwIO $ InSpan s e (UnifyError er)
infer env (P.Var s) =
case Map.lookup s (names env) of
Just (t, _) -> pure (Var s, t)
Nothing -> throwIO (NotInScope s)
infer env (P.App f x) = do
(fun, ty) <- infer env f
funt <- isPiOrPathType ty
case funt of
Left (_, dom, rng) -> do
arg <- check env x dom
let arg' = eval env arg
pure (App fun arg, rng arg')
Right (a, ai0, ai1) -> do
arg <- check env x VI
let arg' = eval env arg
pure (PathP (quote a) (quote ai0) (quote ai1) fun arg, a @@ arg')
infer env (P.Pi s d r) = do
(dom, ty) <- infer env d
case ty of
VType -> pure VType
VTypeω -> pure VTypeω
_ -> throwIO . UnifyError $ NotSort ty
let d' = eval env dom
(rng, rng_t) <-
infer env { names = Map.insert s (makeValueGluingSub d' s) (names env) } r
case ty of
VType -> pure VType
VTypeω -> pure VTypeω
_ -> throwIO . UnifyError $ NotSort ty
pure (Pi s dom rng, rng_t)
infer env (P.Sigma s d r) = do
(dom, ty) <- infer env d
rng_t <-
case ty of
VType -> pure VType
VTypeω -> pure VTypeω
_ -> throwIO . UnifyError $ NotSort ty
let d' = eval env dom
rng <- check env { names = Map.insert s (d', VVar s) (names env) } r rng_t
pure (Sigma s dom rng, rng_t)
infer env P.Type = pure (Type, VType)
infer env P.Typeω = pure (Typeω, VTypeω)
infer env P.I = pure (I, VTypeω)
infer env P.I0 = pure (I0, VI)
infer env P.I1 = pure (I1, VI)
infer env (P.Cut e t) = do
t <- check env t VType
let t' = eval env t
(, t') <$> check env e t'
infer env (P.IAnd x y) = do
x <- check env x VI
y <- check env y VI
pure (IAnd x y, VI)
infer env (P.IOr x y) = do
x <- check env x VI
y <- check env y VI
pure (IOr x y, VI)
infer env P.Path =
pure
( Lam "A" (quote index_t) $
Lam "x" (App (Var "A") I0) $
Lam "y" (App (Var "A") I1) $
Path (Var "A") (Var "x") (Var "y")
, VPi "A" index_t \a ->
VPi "x" (a @@ VI0) \_ ->
VPi "y" (a @@ VI1) (const VType))
infer env P.PartialT =
pure
( Lam "r" I $
Lam "A" Type $
Partial (Var "r") (Var "A")
, VPi "I" VI \i ->
VPi "A" VType (const VTypeω))
infer env P.PartialP =
pure
( Lam "r" I $
Lam "A" (Partial (Var "r") Typeω) $
PartialP (Var "r") (Var "A")
, VPi "phi" VI \i ->
VPi "A" (VPartial i VTypeω) (const VTypeω))
infer env P.Comp = do
let
u_t a r = VPi "i" VI \i -> VPartial r (a @@ i)
index_t :: Value
index_t = VPi "_" VI (const VType)
pure
( Lam "A" (quote index_t) $
Lam "phi" I $
Lam "u" (quote (u_t (VVar "A") (VVar "r"))) $
Lam "a0" (Sub (App (Var "A") I0) (Var "phi") (App (Var "u") I0)) $
Comp (Var "A") (Var "phi") (Var "u") (Var "a0")
, VPi "A" index_t \a ->
VPi "phi" VI \phi ->
VPi "u" (u_t a phi) \u ->
VPi "_" (VSub (a @@ VI0) phi (u @@ VI0)) \_ ->
a @@ VI1
)
infer env P.SubT =
pure
( Lam "A" Type $
Lam "phi" I $
Lam "u" (Partial (Var "phi") (Var "A")) $
Sub (Var "A") (Var "phi") (Var "u")
, VPi "A" VType \a ->
VPi "phi" VI \phi ->
VPi "_" (VPartial phi a) (const VType)
)
infer env P.GlueTy =
pure
( Lam "A" Type $
Lam "phi" I $
Lam "T" (Partial (Var "phi") Type) $
Lam "e" (PartialP (Var "phi") (quote (equiv (VVar "T") (VVar "A")))) $
GlueTy (Var "A") (Var "phi") (Var "T") (Var "e")
, VPi "A" VType \a ->
VPi "phi" VI \phi ->
VPi "T" (VPartial phi VType) \t ->
VPi "e" (VPartialP phi (equiv t a)) \_ ->
VType
)
infer env P.Glue =
pure
( Lam "A" Type $
Lam "phi" I $
Lam "T" (Partial (Var "phi") Type) $
Lam "e" (PartialP (Var "phi") (quote (equiv (VVar "T") (VVar "A")))) $
Lam "t" (PartialP (Var "phi") (Var "T")) $
Lam "a" (Var "A") $
Glue (Var "A") (Var "phi") (Var "T") (Var "e") (Var "t") (Var "a")
, VPi "A" VType \a ->
VPi "phi" VI \phi ->
VPi "T" (VPartial phi VType) \t ->
VPi "e" (VPartialP phi (equiv t a)) \_ ->
VPi "_" (VPartialP phi t) \_ ->
VPi "_" a \_ -> VType
)
infer env P.Unglue =
pure
( Lam "A" Type $
Lam "phi" I $
Lam "T" (Partial (Var "phi") Type) $
Lam "e" (PartialP (Var "phi") (quote (equiv (VVar "T") (VVar "A")))) $
Lam "g" (GlueTy (Var "A") (Var "phi") (Var "T") (Var "e")) $
Unglue (Var "A") (Var "phi") (Var "T") (Var "e") (Var "g")
, VPi "A" VType \a ->
VPi "phi" VI \phi ->
VPi "T" (VPartial phi VType) \t ->
VPi "e" (VPartialP phi (equiv t a)) \e ->
VPi "_" (VGlue a phi t e) \_ -> a
)
infer env P.Bool = pure (Bool, VType)
infer env P.Tt = pure (Tt, VBool)
infer env P.Ff = pure (Ff, VBool)
infer env P.If =
pure
(Lam "P" (Pi "_" Bool Type) $
Lam "x" (App (Var "P") Tt) $
Lam "y" (App (Var "P") Ff) $
Lam "b" Bool $
If (Var "P") (Var "x") (Var "y") (Var "b")
, VPi "P" (VPi "_" VBool (const VType)) \p ->
VPi "_" (p @@ VTrue) \_ ->
VPi "_" (p @@ VFalse) \_ ->
VPi "x" VBool \x -> p @@ x)
infer env (P.INot x) = (, VI) . INot <$> check env x VI
infer env P.Lam{} = error "can't infer type for lambda"
infer env (P.Let v t d b) = do
ty <- check env t VTypeω
let ty' = eval env ty
d <- check env d ty'
let d' = eval env d
(b, t) <- infer env{ names = Map.insert v (ty', d') (names env) } b
pure (Let v ty d b, t)
infer env (P.Proj1 x) = do
(t, ty) <- infer env x
(_, d, _) <- isSigmaType ty
pure (Proj1 t, d)
infer env (P.Proj2 x) = do
(t, ty) <- infer env x
let t' = eval env t
(_, _, r) <- isSigmaType ty
pure (Proj2 t, r (proj1 t'))
formulaToTm :: P.Formula -> P.Exp
formulaToTm (P.Is0 i) = P.INot (P.Var i)
formulaToTm (P.Is1 i) = P.Var i
formulaToTm (P.And x y) = P.IAnd (formulaToTm x) (formulaToTm y)
formulaToTm (P.Or x y) = P.IOr (formulaToTm x) (formulaToTm y)
formulaToTm P.Top = P.I1
formulaToTm P.Bot = P.I0
checkFormula :: Env -> P.Formula -> IO ()
checkFormula env P.Top = pure ()
checkFormula env P.Bot = pure ()
checkFormula env (P.And x y) = checkFormula env x *> checkFormula env y
checkFormula env (P.Or x y) = checkFormula env x *> checkFormula env y
checkFormula env (P.Is0 x) =
case Map.lookup x (names env) of
Just (ty, _) -> unifyTC env ty VI
Nothing -> throwIO (NotInScope x)
checkFormula env (P.Is1 x) =
case Map.lookup x (names env) of
Just (ty, _) -> unifyTC env ty VI
Nothing -> throwIO (NotInScope x)
index_t :: Value
index_t = VPi "_" VI (const VTypeω)