|
{-# 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ω)
|