module LPTypechecker (Type, typecheck) where import Parser (CTerm(..), ITerm(..), Name(..), Program) import Control.Monad (liftM) import Control.Monad.Error import Control.Monad.State import Data.Map (Map, fromList, toList) import Text.PrettyPrint.HughesPJ hiding (parens) import qualified Text.PrettyPrint.HughesPJ as PP typecheck :: Program -> Either String Program typecheck p = liftM (\(prog, _, _) -> prog) $ foldM checkTerm ([], lpve,lpte) p where checkTerm (prog, ve, te) (name, term) = case inferTerm (ve, te) term of Left err -> Left $ "typecheck failure in " ++ name ++ "\n" ++ err Right (annTerm, typ) -> Right ((name, annTerm) : prog, (Global name, evalTerm ve term) : ve, (Global name, typ) : te) -- Yes we're evaluating each term during typechecking, but laziness keeps -- things sane. evalTerm env term = iEval term (env, []) inferTerm env term = iType 0 env term type NameEnv v = [(Name, v)] type Ctx inf = [(Name, inf)] type State v inf = (Bool, String, NameEnv v, Ctx inf) data Value = VLam (Value -> Value) | VStar | VPi Value (Value -> Value) | VNeutral Neutral | VNat | VZero | VSucc Value | VNil Value | VCons Value Value Value Value | VVec Value Value | VEq Value Value Value | VRefl Value Value | VFZero Value | VFSucc Value Value | VFin Value data Neutral = NFree Name | NApp Neutral Value | NNatElim Value Value Value Neutral | NVecElim Value Value Value Value Value Neutral | NEqElim Value Value Value Value Value Neutral | NFinElim Value Value Value Value Neutral type Result a = Either String a type Env = [Value] vapp :: Value -> Value -> Value vapp (VLam f) v = f v vapp (VNeutral n) v = VNeutral (NApp n v) vfree :: Name -> Value vfree n = VNeutral (NFree n) cEval :: CTerm -> (NameEnv Value,Env) -> Value cEval (Inf ii) d = iEval ii d cEval (Lam c) d = VLam (\ x -> cEval c (((\(e, d) -> (e, (x : d))) d))) cEval Zero d = VZero cEval (Succ k) d = VSucc (cEval k d) cEval (Nil a) d = VNil (cEval a d) cEval (Cons a n x xs) d = VCons (cEval a d) (cEval n d) (cEval x d) (cEval xs d) cEval (Refl a x) d = VRefl (cEval a d) (cEval x d) cEval (FZero n) d = VFZero (cEval n d) cEval (FSucc n f) d = VFSucc (cEval n d) (cEval f d) iEval :: ITerm -> (NameEnv Value,Env) -> Value iEval (Ann c _) d = cEval c d iEval Star d = VStar iEval (Pi ty ty') d = VPi (cEval ty d) (\ x -> cEval ty' (((\(e, d) -> (e, (x : d))) d))) iEval (Free x) d = case lookup x (fst d) of Nothing -> (vfree x); Just v -> v iEval (Bound ii) d = (snd d) !! ii iEval (i :$: c) d = vapp (iEval i d) (cEval c d) iEval Nat d = VNat iEval (NatElim m mz ms n) d = let mzVal = cEval mz d msVal = cEval ms d rec nVal = case nVal of VZero -> mzVal VSucc k -> msVal `vapp` k `vapp` rec k VNeutral n -> VNeutral (NNatElim (cEval m d) mzVal msVal n) _ -> error "internal: eval natElim" in rec (cEval n d) iEval (Vec a n) d = VVec (cEval a d) (cEval n d) iEval (VecElim a m mn mc n xs) d = let mnVal = cEval mn d mcVal = cEval mc d rec nVal xsVal = case xsVal of VNil _ -> mnVal VCons _ k x xs -> foldl vapp mcVal [k, x, xs, rec k xs] VNeutral n -> VNeutral (NVecElim (cEval a d) (cEval m d) mnVal mcVal nVal n) _ -> error "internal: eval vecElim" in rec (cEval n d) (cEval xs d) iEval (Eq a x y) d = VEq (cEval a d) (cEval x d) (cEval y d) iEval (EqElim a m mr x y eq) d = let mrVal = cEval mr d rec eqVal = case eqVal of VRefl _ z -> mrVal `vapp` z VNeutral n -> VNeutral (NEqElim (cEval a d) (cEval m d) mrVal (cEval x d) (cEval y d) n) _ -> error "internal: eval eqElim" in rec (cEval eq d) iEval (Fin n) d = VFin (cEval n d) iEval (FinElim m mz ms n f) d = let mzVal = cEval mz d msVal = cEval ms d rec fVal = case fVal of VFZero k -> mzVal `vapp` k VFSucc k g -> foldl vapp msVal [k, g, rec g] VNeutral n' -> VNeutral (NFinElim (cEval m d) (cEval mz d) (cEval ms d) (cEval n d) n') _ -> error "internal: eval finElim" in rec (cEval f d) iSubst :: Int -> ITerm -> ITerm -> ITerm iSubst ii i' (Ann c c') = Ann (cSubst ii i' c) (cSubst ii i' c') iSubst ii r Star = Star iSubst ii r (Pi ty ty') = Pi (cSubst ii r ty) (cSubst (ii + 1) r ty') iSubst ii i' (Bound j) = if ii == j then i' else Bound j iSubst ii i' (Free y) = Free y iSubst ii i' (i :$: c) = iSubst ii i' i :$: cSubst ii i' c iSubst ii r Nat = Nat iSubst ii r (NatElim m mz ms n) = NatElim (cSubst ii r m) (cSubst ii r mz) (cSubst ii r ms) (cSubst ii r ms) iSubst ii r (Vec a n) = Vec (cSubst ii r a) (cSubst ii r n) iSubst ii r (VecElim a m mn mc n xs) = VecElim (cSubst ii r a) (cSubst ii r m) (cSubst ii r mn) (cSubst ii r mc) (cSubst ii r n) (cSubst ii r xs) iSubst ii r (Eq a x y) = Eq (cSubst ii r a) (cSubst ii r x) (cSubst ii r y) iSubst ii r (EqElim a m mr x y eq) = VecElim (cSubst ii r a) (cSubst ii r m) (cSubst ii r mr) (cSubst ii r x) (cSubst ii r y) (cSubst ii r eq) iSubst ii r (Fin n) = Fin (cSubst ii r n) iSubst ii r (FinElim m mz ms n f) = FinElim (cSubst ii r m) (cSubst ii r mz) (cSubst ii r ms) (cSubst ii r n) (cSubst ii r f) cSubst :: Int -> ITerm -> CTerm -> CTerm cSubst ii i' (Inf i) = Inf (iSubst ii i' i) cSubst ii i' (Lam c) = Lam (cSubst (ii + 1) i' c) cSubst ii r Zero = Zero cSubst ii r (Succ n) = Succ (cSubst ii r n) cSubst ii r (Nil a) = Nil (cSubst ii r a) cSubst ii r (Cons a n x xs) = Cons (cSubst ii r a) (cSubst ii r x) (cSubst ii r x) (cSubst ii r xs) cSubst ii r (Refl a x) = Refl (cSubst ii r a) (cSubst ii r x) cSubst ii r (FZero n) = FZero (cSubst ii r n) cSubst ii r (FSucc n k) = FSucc (cSubst ii r n) (cSubst ii r k) quote :: Int -> Value -> CTerm quote ii (VLam t) = Lam (quote (ii + 1) (t (vfree (Quote ii)))) quote ii VStar = Inf Star quote ii (VPi v f) = Inf (Pi (quote ii v) (quote (ii + 1) (f (vfree (Quote ii))))) quote ii (VNeutral n) = Inf (neutralQuote ii n) quote ii VNat = Inf Nat quote ii VZero = Zero quote ii (VSucc n) = Succ (quote ii n) quote ii (VVec a n) = Inf (Vec (quote ii a) (quote ii n)) quote ii (VNil a) = Nil (quote ii a) quote ii (VCons a n x xs) = Cons (quote ii a) (quote ii n) (quote ii x) (quote ii xs) quote ii (VEq a x y) = Inf (Eq (quote ii a) (quote ii x) (quote ii y)) quote ii (VRefl a x) = Refl (quote ii a) (quote ii x) quote ii (VFin n) = Inf (Fin (quote ii n)) quote ii (VFZero n) = FZero (quote ii n) quote ii (VFSucc n f) = FSucc (quote ii n) (quote ii f) neutralQuote :: Int -> Neutral -> ITerm neutralQuote ii (NFree v) = boundfree ii v neutralQuote ii (NApp n v) = neutralQuote ii n :$: quote ii v neutralQuote ii (NNatElim m z s n) = NatElim (quote ii m) (quote ii z) (quote ii s) (Inf (neutralQuote ii n)) neutralQuote ii (NVecElim a m mn mc n xs) = VecElim (quote ii a) (quote ii m) (quote ii mn) (quote ii mc) (quote ii n) (Inf (neutralQuote ii xs)) neutralQuote ii (NEqElim a m mr x y eq) = EqElim (quote ii a) (quote ii m) (quote ii mr) (quote ii x) (quote ii y) (Inf (neutralQuote ii eq)) neutralQuote ii (NFinElim m mz ms n f) = FinElim (quote ii m) (quote ii mz) (quote ii ms) (quote ii n) (Inf (neutralQuote ii f)) boundfree :: Int -> Name -> ITerm boundfree ii (Quote k) = Bound ((ii - k - 1) `max` 0) boundfree ii x = Free x instance Show Value where show = show . quote0 type Type = Value type Context = [(Name, Type)] quote0 :: Value -> CTerm quote0 = quote 0 lpte :: Ctx Value lpte = [(Global "Zero", VNat), (Global "Succ", VPi VNat (\ _ -> VNat)), (Global "Nat", VStar), (Global "natElim", VPi (VPi VNat (\ _ -> VStar)) (\ m -> VPi (m `vapp` VZero) (\ _ -> VPi (VPi VNat (\ k -> VPi (m `vapp` k) (\ _ -> (m `vapp` (VSucc k))))) ( \ _ -> VPi VNat (\ n -> m `vapp` n))))), (Global "Nil", VPi VStar (\ a -> VVec a VZero)), (Global "Cons", VPi VStar (\ a -> VPi VNat (\ n -> VPi a (\ _ -> VPi (VVec a n) (\ _ -> VVec a (VSucc n)))))), (Global "Vec", VPi VStar (\ _ -> VPi VNat (\ _ -> VStar))), (Global "vecElim", VPi VStar (\ a -> VPi (VPi VNat (\ n -> VPi (VVec a n) (\ _ -> VStar))) (\ m -> VPi (m `vapp` VZero `vapp` (VNil a)) (\ _ -> VPi (VPi VNat (\ n -> VPi a (\ x -> VPi (VVec a n) (\ xs -> VPi (m `vapp` n `vapp` xs) (\ _ -> m `vapp` VSucc n `vapp` VCons a n x xs))))) (\ _ -> VPi VNat (\ n -> VPi (VVec a n) (\ xs -> m `vapp` n `vapp` xs))))))), (Global "Refl", VPi VStar (\ a -> VPi a (\ x -> VEq a x x))), (Global "Eq", VPi VStar (\ a -> VPi a (\ x -> VPi a (\ y -> VStar)))), (Global "eqElim", VPi VStar (\ a -> VPi (VPi a (\ x -> VPi a (\ y -> VPi (VEq a x y) (\ _ -> VStar)))) (\ m -> VPi (VPi a (\ x -> m `vapp` x `vapp` x `vapp` VRefl a x)) (\ _ -> VPi a (\ x -> VPi a (\ y -> VPi (VEq a x y) (\ eq -> m `vapp` x `vapp` y `vapp` eq))))))), (Global "FZero", VPi VNat (\ n -> VFin (VSucc n))), (Global "FSucc", VPi VNat (\ n -> VPi (VFin n) (\ f -> VFin (VSucc n)))), (Global "Fin", VPi VNat (\ n -> VStar)), (Global "finElim", VPi (VPi VNat (\ n -> VPi (VFin n) (\ _ -> VStar))) (\ m -> VPi (VPi VNat (\ n -> m `vapp` (VSucc n) `vapp` (VFZero n))) (\ _ -> VPi (VPi VNat (\ n -> VPi (VFin n) (\ f -> VPi (m `vapp` n `vapp` f) (\ _ -> m `vapp` (VSucc n) `vapp` (VFSucc n f))))) (\ _ -> VPi VNat (\ n -> VPi (VFin n) (\ f -> m `vapp` n `vapp` f))))))] lpve :: Ctx Value lpve = [(Global "Zero", VZero), (Global "Succ", VLam (\ n -> VSucc n)), (Global "Nat", VNat), (Global "natElim", cEval (Lam (Lam (Lam (Lam (Inf (NatElim (Inf (Bound 3)) (Inf (Bound 2)) (Inf (Bound 1)) (Inf (Bound 0)))))))) ([], [])), (Global "Nil", VLam (\ a -> VNil a)), (Global "Cons", VLam (\ a -> VLam (\ n -> VLam (\ x -> VLam (\ xs -> VCons a n x xs))))), (Global "Vec", VLam (\ a -> VLam (\ n -> VVec a n))), (Global "vecElim", cEval (Lam (Lam (Lam (Lam (Lam (Lam (Inf (VecElim (Inf (Bound 5)) (Inf (Bound 4)) (Inf (Bound 3)) (Inf (Bound 2)) (Inf (Bound 1)) (Inf (Bound 0)))))))))) ([],[])), (Global "Refl", VLam (\ a -> VLam (\ x -> VRefl a x))), (Global "Eq", VLam (\ a -> VLam (\ x -> VLam (\ y -> VEq a x y)))), (Global "eqElim", cEval (Lam (Lam (Lam (Lam (Lam (Lam (Inf (EqElim (Inf (Bound 5)) (Inf (Bound 4)) (Inf (Bound 3)) (Inf (Bound 2)) (Inf (Bound 1)) (Inf (Bound 0)))))))))) ([],[])), (Global "FZero", VLam (\ n -> VFZero n)), (Global "FSucc", VLam (\ n -> VLam (\ f -> VFSucc n f))), (Global "Fin", VLam (\ n -> VFin n)), (Global "finElim", cEval (Lam (Lam (Lam (Lam (Lam (Inf (FinElim (Inf (Bound 4)) (Inf (Bound 3)) (Inf (Bound 2)) (Inf (Bound 1)) (Inf (Bound 0))))))))) ([],[]))] iType :: Int -> (NameEnv Value,Context) -> ITerm -> Result (ITerm, Type) iType ii g (Ann e tyt ) = do tyt' <- cType ii g tyt VStar let ty = cEval tyt (fst g, []) e' <- cType ii g e ty return (Ann e tyt, ty) iType ii g Star = return (Star, VStar) -- Any fold instances *within* a type will go away during type compilation, -- so we need not save those AST tranformations. iType ii g pi@(Pi tyt tyt') = do cType ii g tyt VStar let ty = cEval tyt (fst g, []) cType (ii + 1) ((\ (d,g) -> (d, ((Local ii, ty) : g))) g) (cSubst 0 (Free (Local ii)) tyt') VStar return (pi, VStar) iType ii g (Free x) = case lookup x (snd g) of Just ty -> return (Free x, ty) Nothing -> throwError ("unknown identifier: " ++ render (iPrint 0 0 (Free x))) iType ii g (e1 :$: e2) = do (e1', lhsType) <- iType ii g e1 case lhsType of VPi ty ty' -> do cType ii g e2 ty return ( e1' :$: e2, ty' (cEval e2 (fst g, []))) _ -> throwError "illegal application" iType ii g Nat = return (Nat, VStar) iType ii g (NatElim m mz ms n) = do cType ii g m (VPi VNat (const VStar)) let mVal = cEval m (fst g, []) mz' <- cType ii g mz (mVal `vapp` VZero) ms' <- cType ii g ms (VPi VNat (\ k -> VPi (mVal `vapp` k) (\ _ -> mVal `vapp` VSucc k))) n' <- cType ii g n VNat let nVal = cEval n (fst g, []) return (NatElim (quote ii mVal) mz' ms' n', (mVal `vapp` nVal)) iType ii g (Vec a n) = do a' <- cType ii g a VStar n' <- cType ii g n VNat return (Vec a' n', VStar) iType ii g (VecElim a m mn mc n vs) = do a' <- cType ii g a VStar let aVal = cEval a (fst g, []) m' <- cType ii g m ( VPi VNat (\n -> VPi (VVec aVal n) (\ _ -> VStar))) let mVal = cEval m (fst g, []) mn' <- cType ii g mn (foldl vapp mVal [VZero, VNil aVal]) mc' <- cType ii g mc ( VPi VNat (\ n -> VPi aVal (\ y -> VPi (VVec aVal n) (\ ys -> VPi (foldl vapp mVal [n, ys]) (\ _ -> (foldl vapp mVal [VSucc n, VCons aVal n y ys])))))) n' <- cType ii g n VNat let nVal = cEval n (fst g, []) vs' <- cType ii g vs (VVec aVal nVal) let vsVal = cEval vs (fst g, []) return (VecElim (quote ii aVal) (quote ii mVal) mn' mc' n' vs', foldl vapp mVal [nVal, vsVal]) iType i g (Eq a x y) = do a' <- cType i g a VStar let aVal = cEval a (fst g, []) x' <- cType i g x aVal y' <- cType i g y aVal return (Eq a' x' y', VStar) iType i g (EqElim a m mr x y eq) = do a' <- cType i g a VStar let aVal = cEval a (fst g, []) m' <- cType i g m (VPi aVal (\ x -> VPi aVal (\ y -> VPi (VEq aVal x y) (\ _ -> VStar)))) let mVal = cEval m (fst g, []) mr' <- cType i g mr (VPi aVal (\ x -> foldl vapp mVal [x, x])) x' <- cType i g x aVal let xVal = cEval x (fst g, []) y' <- cType i g y aVal let yVal = cEval y (fst g, []) eq' <- cType i g eq (VEq aVal xVal yVal) let eqVal = cEval eq (fst g, []) return (EqElim (quote i aVal) (quote i mVal) mr' x' y' eq', foldl vapp mVal [xVal, yVal]) cType :: Int -> (NameEnv Value,Context) -> CTerm -> Type -> Result CTerm cType ii g (Inf e) v = do (e', v') <- iType ii g e unless ( quote0 v == quote0 v') (throwError ("type mismatch:\n" ++ "type inferred: " ++ render (cPrint 0 0 (quote0 v')) ++ "\n" ++ "type expected: " ++ render (cPrint 0 0 (quote0 v)) ++ "\n" ++ "for expression: " ++ render (iPrint 0 0 e))) return (Inf e') cType ii g (Lam e) ( VPi ty ty') = cType (ii + 1) ((\ (d,g) -> (d, ((Local ii, ty ) : g))) g) (cSubst 0 (Free (Local ii)) e) ( ty' (vfree (Local ii))) cType ii g Zero VNat = return Zero cType ii g (Succ k) VNat = cType ii g k VNat cType ii g (Nil a) (VVec bVal VZero) = do a' <- cType ii g a VStar let aVal = cEval a (fst g, []) unless (quote0 aVal == quote0 bVal) (throwError "type mismatch") return a' cType ii g (Cons a n x xs) (VVec bVal (VSucc k)) = do a' <- cType ii g a VStar let aVal = cEval a (fst g, []) unless (quote0 aVal == quote0 bVal) (throwError "type mismatch") n' <- cType ii g n VNat let nVal = cEval n (fst g, []) unless (quote0 nVal == quote0 k) (throwError "number mismatch") x' <- cType ii g x aVal xs' <- cType ii g xs (VVec bVal k) return (Cons a' n' x' xs') cType ii g (Refl a z) (VEq bVal xVal yVal) = do a' <- cType ii g a VStar let aVal = cEval a (fst g, []) unless (quote0 aVal == quote0 bVal) (throwError "type mismatch") z' <- cType ii g z aVal let zVal = cEval z (fst g, []) unless (quote0 zVal == quote0 xVal && quote0 zVal == quote0 yVal) (throwError "type mismatch") return (Refl a' z') cType ii g _ _ = throwError "type mismatch" {- Printing -} vars :: [String] vars = [ c : n | n <- "" : map show [1..], c <- ['x','y','z'] ++ ['a'..'w'] ] parensIf :: Bool -> Doc -> Doc parensIf True = PP.parens parensIf False = id iPrint :: Int -> Int -> ITerm -> Doc iPrint p ii (Ann c ty) = parensIf (p > 1) (cPrint 2 ii c <> text " :: " <> cPrint 0 ii ty) iPrint p ii Star = text "*" iPrint p ii (Pi d (Inf (Pi d' r))) = parensIf (p > 0) (nestedForall (ii + 2) [(ii + 1, d'), (ii, d)] r) iPrint p ii (Pi d r) = parensIf (p > 0) (sep [text "forall " <> text (vars !! ii) <> text " :: " <> cPrint 0 ii d <> text " .", cPrint 0 (ii + 1) r]) iPrint p ii (Bound k) = text (vars !! (ii - k - 1)) iPrint p ii (Free (Global s))= text s iPrint p ii (i :$: c) = parensIf (p > 2) (sep [iPrint 2 ii i, nest 2 (cPrint 3 ii c)]) iPrint p ii Nat = text "Nat" iPrint p ii (NatElim m z s n)= iPrint p ii (Free (Global "natElim") :$: m :$: z :$: s :$: n) iPrint p ii (Vec a n) = iPrint p ii (Free (Global "Vec") :$: a :$: n) iPrint p ii (VecElim a m mn mc n xs) = iPrint p ii (Free (Global "vecElim") :$: a :$: m :$: mn :$: mc :$: n :$: xs) iPrint p ii (Eq a x y) = iPrint p ii (Free (Global "Eq") :$: a :$: x :$: y) iPrint p ii (EqElim a m mr x y eq) = iPrint p ii (Free (Global "eqElim") :$: a :$: m :$: mr :$: x :$: y :$: eq) iPrint p ii (Fin n) = iPrint p ii (Free (Global "Fin") :$: n) iPrint p ii (FinElim m mz ms n f) = iPrint p ii (Free (Global "finElim") :$: m :$: mz :$: ms :$: n :$: f) iPrint p ii x = text ("[" ++ show x ++ "]") cPrint :: Int -> Int -> CTerm -> Doc cPrint p ii (Inf i) = iPrint p ii i cPrint p ii (Lam c) = parensIf (p > 0) (text "\\ " <> text (vars !! ii) <> text " -> " <> cPrint 0 (ii + 1) c) cPrint p ii Zero = fromNat 0 ii Zero -- text "Zero" cPrint p ii (Succ n) = fromNat 0 ii (Succ n) -- iPrint p ii (Free (Global "Succ") :$: n) cPrint p ii (Nil a) = iPrint p ii (Free (Global "Nil") :$: a) cPrint p ii (Cons a n x xs) = iPrint p ii (Free (Global "Cons") :$: a :$: n :$: x :$: xs) cPrint p ii (Refl a x) = iPrint p ii (Free (Global "Refl") :$: a :$: x) cPrint p ii (FZero n) = iPrint p ii (Free (Global "FZero") :$: n) cPrint p ii (FSucc n f)= iPrint p ii (Free (Global "FSucc") :$: n :$: f) fromNat :: Int -> Int -> CTerm -> Doc fromNat n ii Zero = int n fromNat n ii (Succ k) = fromNat (n + 1) ii k fromNat n ii t = parensIf True (int n <> text " + " <> cPrint 0 ii t) nestedForall :: Int -> [(Int, CTerm)] -> CTerm -> Doc nestedForall ii ds (Inf (Pi d r)) = nestedForall (ii + 1) ((ii, d) : ds) r nestedForall ii ds x = sep [text "forall " <> sep [parensIf True (text (vars !! n) <> text " :: " <> cPrint 0 n d) | (n,d) <- reverse ds] <> text " .", cPrint 0 ii x]