initial commit

This commit is contained in:
Rachel Lambda Samuelsson 2022-05-12 18:52:25 +02:00
commit d701a81172
2 changed files with 206 additions and 0 deletions

201
Algo.hs Normal file
View File

@ -0,0 +1,201 @@
{-# LANGUAGE LambdaCase #-}
module Algo where
import Data.Maybe (fromMaybe)
import Data.Map (Map)
import qualified Data.Map as M
import Control.Monad.RWS
import Control.Monad.Except
import Data.List
type Id = String
data Exp
= Var Id
| App Exp Exp
| Abs Id Exp
| Let Id Exp Exp Exp
| Pi Id Exp Exp
| Type
deriving Show
data Val
= VGen Int
| VApp Val Val
| VType
| VClos Env Exp
deriving Show
type Env = Map Id Val
data Err
= Lookup
| ExpectedPi
| ExpectedType
| NoInfer
| AppError
deriving Show
data Result = Result (Either Err Bool) [String]
instance Show Result where
show (Result r s) = header <> "\n" <> intercalate "\n" s
where header = case r of
Left e -> show e
Right True -> "Checked successfully!"
Right False -> "Types do not match!"
type Conv = ExceptT Err (RWS Env [String] Int)
type Check = ExceptT Err (RWS (Env, Env) [String] Int)
li :: Conv a -> Check a
li conv = do
state <- get
(env,_) <- ask
let (a, s, w) = runRWS (runExceptT conv) env state
put s
tell w
case a of
Left e -> throwError e
Right v -> pure v
maybeConv :: MonadError e m => e -> Maybe a -> m a
maybeConv _ (Just a) = pure a
maybeConv e Nothing = throwError e
lookupEnv :: Id -> Conv Val
lookupEnv x = ask >>= maybeConv Lookup . M.lookup x
insertEnv :: Id -> Val -> Conv a -> Conv a
insertEnv x v = local (M.insert x v)
withEnv :: Env -> Conv a -> Conv a
withEnv = local . const
localCh :: (Env -> Env, Env -> Env) -> Check a -> Check a
localCh (f,g) = local h
where h (a,b) = (f a, g b)
fresh :: (Num i, MonadState i m) => m i
fresh = do
i <- get
put (i+1)
pure i
app :: Val -> Val -> Conv Val
app u v = case u of
VClos env (Abs x e) -> insertEnv x v (eval e)
_ -> pure (VApp u v)
eval :: Exp -> Conv Val
eval = \case
Var x -> lookupEnv x
App e1 e2 -> do
e1' <- eval e1
e2' <- eval e2
app e1' e2'
Let x e1 _ e3 -> do
e1' <- eval e1
insertEnv x e1' (eval e3)
Type -> pure (VType)
e -> ask >>= pure . (flip VClos) e
whnf :: Val -> Conv Val
whnf = \case
VApp u w -> do
u' <- whnf u
w' <- whnf w
app u' w'
VClos env e -> withEnv env (eval e)
v -> pure v
eqVal :: Val -> Val -> Conv Bool
eqVal u1 u2 = do
u1' <- whnf u1
u2' <- whnf u2
tell ["checking equality of terms '" <> show u1 <> "' and '" <> show u2 <> "'."]
tell ["with value representations '" <> show u1' <> "' and '" <> show u2' <> "'."]
case (u1', u2') of
(VType, VType) -> pure True
(VApp t1 w1, VApp t2 w2) -> (&&) <$> eqVal t1 t2 <*> eqVal w1 w2
(VGen k1, VGen k2) -> pure (k1 == k2)
(VClos env1 (Abs x1 e1), VClos env2 (Abs x2 e2)) -> do
v <- VGen <$> fresh
eqVal (VClos (M.insert x1 v env1) e1)
(VClos (M.insert x2 v env2) e2)
(VClos env1 (Pi x1 a1 b1), VClos env2 (Pi x2 a2 b2)) -> do
v <- VGen <$> fresh
(&&) <$> eqVal (VClos env1 a1)
(VClos env2 a2)
<*> eqVal (VClos (M.insert x1 v env1) b1)
(VClos (M.insert x1 v env2) b2)
_ -> pure False
checkType :: Exp -> Check Bool
checkType = (flip checkExp) VType
checkExp :: Exp -> Val -> Check Bool
checkExp e v' = do
v <- li (whnf v')
case e of
Abs x n ->
case v of
VClos env (Pi y a b) -> do
v <- VGen <$> fresh
localCh (M.insert x v, M.insert x (VClos env a))
(checkExp n (VClos (M.insert y v env) b))
_ -> throwError ExpectedPi
Pi x a b ->
case v of
VType -> do
v <- VGen <$> fresh
rho <- fst <$> ask
(&&) <$> checkType a <*> localCh (M.insert x v, M.insert x (VClos rho a)) (checkType b)
_ -> throwError ExpectedType
Let x e1 e2 e3 -> do
etr <- li (eval e1)
ety <- li (eval e2)
(&&) <$> checkType e2 <*> localCh (M.insert x etr, M.insert x ety) (checkExp e3 v)
_ -> do
e' <- inferExp e
li (eqVal v e')
inferExp :: Exp -> Check Val
inferExp = \case
Var id -> maybeConv Lookup . M.lookup id =<< snd <$> ask
App e1 e2 ->
inferExp e1 >>= li . whnf >>= \case
VClos env (Pi x a b) -> do
rho <- fst <$> ask
bool <- checkExp e2 (VClos env a)
if bool
then pure (VClos (M.insert x (VClos rho e2) env) b)
else throwError AppError
_ -> throwError ExpectedPi
Type -> pure VType
_ -> throwError NoInfer
typecheck :: Exp -> Exp -> Result
typecheck m a = resolve ((&&) <$> checkType a <*> checkExp m (VClos M.empty a))
where resolve action = let (r,_,w) = (runRWS (runExceptT action) (M.empty, M.empty) 0)
in Result r w
test1 :: Result
test1 = typecheck (Abs "A" (Abs "x" (Var "x")))
(Pi "A" Type (Pi "x" (Var "A") (Var "A")))
test2 :: Result
test2 = typecheck (Abs "A" (Abs "f" (Abs "x" (App (Var "f") (Var "x")))))
(Pi "A" Type (Pi "f" (Pi "_" (Var "A") (Var "A")) (Pi "x" (Var "A") (Var "A"))))

5
README.md Normal file
View File

@ -0,0 +1,5 @@
# An algorithm for type-checking dependent types
A "modern" implementation of algorithm described in Thierry Coquand's paper. This employs some monad nicities compared to the haskell implementation in the paper (and isn't a pdf).
This isn't meant to be "better", it might even be more confusing to you. I wrote this to get more of a feel for checking dependent types.