@ -0,0 +1,87 @@ | |||||
{-# LANGUAGE TupleSections, OverloadedStrings #-} | |||||
module Elab where | |||||
import Elab.Monad | |||||
import qualified Presyntax.Presyntax as P | |||||
import Syntax | |||||
import Elab.Eval | |||||
infer :: P.Expr -> ElabM (Term, NFType) | |||||
infer (P.Var t) = (Ref (Bound t),) <$> getNfType (Bound t) | |||||
infer (P.App p f x) = do | |||||
(f, f_ty) <- infer f | |||||
(d, r, w) <- isPiType p f_ty | |||||
x <- check x d | |||||
x_nf <- eval x | |||||
pure (App p (w f) x, r x_nf) | |||||
infer (P.Pi p s d r) = do | |||||
d <- check d VType | |||||
d_nf <- eval d | |||||
assume (Bound s) d_nf $ do | |||||
r <- check r VType | |||||
pure (Pi p s d r, VType) | |||||
infer (P.Sigma s d r) = do | |||||
d <- check d VType | |||||
d_nf <- eval d | |||||
assume (Bound s) d_nf $ do | |||||
r <- check r VType | |||||
pure (Sigma s d r, VType) | |||||
infer exp = do | |||||
t <- newMeta VType | |||||
tm <- check exp t | |||||
pure (tm, t) | |||||
check :: P.Expr -> NFType -> ElabM Term | |||||
check (P.Lam p var body) (VPi p' dom (Closure _ rng)) | p == p' = | |||||
assume (Bound var) dom $ | |||||
Lam p var <$> check body (rng (VVar (Bound var))) | |||||
check tm (VPi P.Im dom (Closure var rng)) = | |||||
assume (Bound var) dom $ | |||||
Lam P.Im var <$> check tm (rng (VVar (Bound var))) | |||||
check (P.Lam p v b) ty = do | |||||
(d, r, wp) <- isPiType p ty | |||||
assume (Bound v) d $ | |||||
wp . Lam P.Im v <$> check b (r (VVar (Bound v))) | |||||
check (P.Pair a b) ty = do | |||||
(d, r, wp) <- isSigmaType ty | |||||
a <- check a d | |||||
a_nf <- eval a | |||||
b <- check b (r a_nf) | |||||
pure (wp (Pair a b)) | |||||
check exp ty = do | |||||
(tm, has) <- infer exp | |||||
unify has ty | |||||
pure tm | |||||
isPiType :: P.Plicity -> NFType -> ElabM (Value, NFType -> NFType, Term -> Term) | |||||
isPiType p (VPi p' d (Closure _ k)) | p == p' = pure (d, k, id) | |||||
isPiType p t = do | |||||
dom <- newMeta VType | |||||
name <- newName | |||||
assume (Bound name) dom $ do | |||||
rng <- newMeta VType | |||||
wp <- isConvertibleTo t (VPi p dom (Closure name (const rng))) | |||||
pure (dom, const rng, wp) | |||||
isSigmaType :: NFType -> ElabM (Value, NFType -> NFType, Term -> Term) | |||||
isSigmaType (VSigma d (Closure _ k)) = pure (d, k, id) | |||||
isSigmaType t = do | |||||
dom <- newMeta VType | |||||
name <- newName | |||||
assume (Bound name) dom $ do | |||||
rng <- newMeta VType | |||||
wp <- isConvertibleTo t (VSigma dom (Closure name (const rng))) | |||||
pure (dom, const rng, wp) | |||||
identityTy :: NFType | |||||
identityTy = VPi P.Im VType (Closure "A" $ \t -> VPi P.Ex t (Closure "_" (const t))) |
@ -0,0 +1,201 @@ | |||||
{-# LANGUAGE LambdaCase #-} | |||||
{-# LANGUAGE DeriveAnyClass #-} | |||||
module Elab.Eval where | |||||
import Control.Monad.Reader | |||||
import Control.Exception | |||||
import qualified Data.Map.Strict as Map | |||||
import qualified Data.Set as Set | |||||
import qualified Data.Text as T | |||||
import Data.Traversable | |||||
import Data.Set (Set) | |||||
import Data.Typeable | |||||
import Data.Foldable | |||||
import Data.IORef | |||||
import Data.Maybe | |||||
import Elab.Monad | |||||
import Presyntax.Presyntax (Plicity(..)) | |||||
import Syntax | |||||
import System.IO.Unsafe | |||||
eval :: Term -> ElabM Value | |||||
eval t = asks (flip evalWithEnv t) | |||||
forceIO :: MonadIO m => Value -> m Value | |||||
forceIO vl@(VNe (HMeta (MV _ cell)) args) = do | |||||
solved <- liftIO $ readIORef cell | |||||
case solved of | |||||
Just vl -> forceIO $ foldl applProj vl (reverse args) | |||||
Nothing -> pure vl | |||||
forceIO x = pure x | |||||
applProj :: Value -> Projection -> Value | |||||
applProj fun (PApp p arg) = vApp p fun arg | |||||
applProj fun PProj1 = vProj1 fun | |||||
applProj fun PProj2 = vProj2 fun | |||||
force :: Value -> Value | |||||
force = unsafePerformIO . forceIO | |||||
evalWithEnv :: ElabEnv -> Term -> Value | |||||
evalWithEnv env (Ref x) = | |||||
case Map.lookup x (getEnv env) of | |||||
Just (_, vl) -> vl | |||||
_ -> error "variable not in scope when evaluating" | |||||
evalWithEnv env (App p f x) = vApp p (evalWithEnv env f) (evalWithEnv env x) | |||||
evalWithEnv env (Lam p s t) = | |||||
VLam p $ Closure s $ \a -> | |||||
evalWithEnv (ElabEnv (Map.insert (Bound s) (error "type of abs", a) (getEnv env))) t | |||||
evalWithEnv env (Pi p s d t) = | |||||
VPi p (evalWithEnv env d) $ Closure s $ \a -> | |||||
evalWithEnv (ElabEnv (Map.insert (Bound s) (error "type of abs", a) (getEnv env))) t | |||||
evalWithEnv _ (Meta m) = VNe (HMeta m) [] | |||||
evalWithEnv env (Sigma s d t) = | |||||
VSigma (evalWithEnv env d) $ Closure s $ \a -> | |||||
evalWithEnv (ElabEnv (Map.insert (Bound s) (error "type of abs", a) (getEnv env))) t | |||||
evalWithEnv e (Pair a b) = VPair (evalWithEnv e a) (evalWithEnv e b) | |||||
evalWithEnv e (Proj1 a) = vProj1 (evalWithEnv e a) | |||||
evalWithEnv e (Proj2 a) = vProj2 (evalWithEnv e a) | |||||
evalWithEnv _ Type = VType | |||||
vApp :: Plicity -> Value -> Value -> Value | |||||
vApp p (VLam p' k) arg = assert (p == p') $ clCont k arg | |||||
vApp p (VNe h sp) arg = VNe h (PApp p arg:sp) | |||||
vApp _ x _ = error $ "can't apply " ++ show x | |||||
vProj1 :: Value -> Value | |||||
vProj1 (VPair a _) = a | |||||
vProj1 (VNe h sp) = VNe h (PProj1:sp) | |||||
vProj1 x = error $ "can't proj1 " ++ show x | |||||
vProj2 :: Value -> Value | |||||
vProj2 (VPair _ b) = b | |||||
vProj2 (VNe h sp) = VNe h (PProj2:sp) | |||||
vProj2 x = error $ "can't proj2 " ++ show x | |||||
data NotEqual = NotEqual Value Value | |||||
deriving (Show, Typeable, Exception) | |||||
unify :: Value -> Value -> ElabM () | |||||
unify topa topb = join $ go <$> forceIO topa <*> forceIO topb where | |||||
go (VNe (HMeta mv) sp) rhs = solveMeta mv sp rhs | |||||
go (VNe x a) (VNe x' a') | |||||
| x == x', length a == length a' = | |||||
traverse_ (uncurry unifySpine) (zip a a') | |||||
| otherwise = fail | |||||
go (VLam p (Closure _ k)) (VLam p' (Closure _ k')) | p == p' = do | |||||
t <- VVar . Bound <$> newName | |||||
unify (k t) (k' t) | |||||
go (VPi p d (Closure _ k)) (VPi p' d' (Closure _ k')) | p == p' = do | |||||
t <- VVar . Bound <$> newName | |||||
unify d d' | |||||
unify (k t) (k' t) | |||||
go _ _ = fail | |||||
fail = liftIO . throwIO $ NotEqual topa topb | |||||
unifySpine (PApp a v) (PApp a' v') | |||||
| a == a' = unify v v' | |||||
unifySpine _ _ = fail | |||||
isConvertibleTo :: Value -> Value -> ElabM (Term -> Term) | |||||
VPi Im d (Closure _v k) `isConvertibleTo` ty = do | |||||
meta <- newMeta d | |||||
cont <- k meta `isConvertibleTo` ty | |||||
pure (\f -> cont (App Ex f (quote meta))) | |||||
isConvertibleTo a b = do | |||||
unify a b | |||||
pure id | |||||
newMeta :: Value -> ElabM Value | |||||
newMeta _dom = do | |||||
n <- newName | |||||
c <- liftIO $ newIORef Nothing | |||||
let m = MV n c | |||||
env <- asks getEnv | |||||
t <- for (Map.toList env) $ \(n, (_, c)) -> pure $ | |||||
case c of | |||||
VVar n' | n == n' -> Just (PApp Ex (VVar n')) | |||||
_ -> Nothing | |||||
pure (VNe (HMeta m) (catMaybes t)) | |||||
newName :: MonadIO m => m T.Text | |||||
newName = liftIO $ do | |||||
x <- atomicModifyIORef _nameCounter $ \x -> (x + 1, x + 1) | |||||
pure (T.pack (show x)) | |||||
_nameCounter :: IORef Int | |||||
_nameCounter = unsafePerformIO $ newIORef 0 | |||||
{-# NOINLINE _nameCounter #-} | |||||
solveMeta :: MV -> [Projection] -> Value -> ElabM () | |||||
solveMeta m@(MV _ cell) sp rhs = do | |||||
liftIO $ print (m, sp, rhs) | |||||
names <- checkSpine Set.empty sp | |||||
checkScope (Set.fromList (Bound <$> names)) rhs | |||||
let tm = quote rhs | |||||
lam = evalWithEnv emptyEnv $ foldr (Lam Ex) tm names | |||||
liftIO . atomicModifyIORef' cell $ \case | |||||
Just _ -> error "filled cell in solvedMeta" | |||||
Nothing -> (Just lam, ()) | |||||
checkScope :: Set Name -> Value -> ElabM () | |||||
checkScope scope (VNe h sp) = | |||||
do | |||||
case h of | |||||
HVar v -> | |||||
unless (v `Set.member` scope) . liftIO . throwIO $ | |||||
NotInScope v | |||||
HMeta{} -> pure () | |||||
traverse_ checkProj sp | |||||
where | |||||
checkProj (PApp _ t) = checkScope scope t | |||||
checkProj PProj1 = pure () | |||||
checkProj PProj2 = pure () | |||||
checkScope scope (VLam _ (Closure n k)) = | |||||
checkScope (Set.insert (Bound n) scope) (k (VVar (Bound n))) | |||||
checkScope scope (VPi _ d (Closure n k)) = do | |||||
checkScope scope d | |||||
checkScope (Set.insert (Bound n) scope) (k (VVar (Bound n))) | |||||
checkScope scope (VSigma d (Closure n k)) = do | |||||
checkScope scope d | |||||
checkScope (Set.insert (Bound n) scope) (k (VVar (Bound n))) | |||||
checkScope s (VPair a b) = traverse_ (checkScope s) [a, b] | |||||
checkScope _ VType = pure () | |||||
checkSpine :: Set Name -> [Projection] -> ElabM [T.Text] | |||||
checkSpine scope (PApp Ex (VVar n@(Bound t)):xs) | |||||
| n `Set.member` scope = liftIO . throwIO $ NonLinearSpine n | |||||
| otherwise = (t:) <$> checkSpine scope xs | |||||
checkSpine _ (p:_) = liftIO . throwIO $ SpineProj p | |||||
checkSpine _ [] = pure [] | |||||
newtype NonLinearSpine = NonLinearSpine { getDupeName :: Name } | |||||
deriving (Show, Typeable, Exception) | |||||
newtype SpineProjection = SpineProj { getSpineProjection :: Projection } | |||||
deriving (Show, Typeable, Exception) |
@ -0,0 +1,48 @@ | |||||
{-# LANGUAGE DeriveAnyClass #-} | |||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-} | |||||
{-# LANGUAGE DerivingVia #-} | |||||
module Elab.Monad where | |||||
import Control.Monad.Reader | |||||
import Control.Exception | |||||
import qualified Data.Map.Strict as Map | |||||
import Data.Map.Strict (Map) | |||||
import Data.Typeable | |||||
import Syntax | |||||
newtype ElabEnv = ElabEnv { getEnv :: Map Name (NFType, Value) } | |||||
newtype ElabM a = ElabM { runElab :: ElabEnv -> IO a } | |||||
deriving (Functor, Applicative, Monad, MonadReader ElabEnv, MonadIO) | |||||
via ReaderT ElabEnv IO | |||||
newtype NotInScope = NotInScope { getName :: Name } | |||||
deriving (Show, Typeable) | |||||
deriving anyclass (Exception) | |||||
emptyEnv :: ElabEnv | |||||
emptyEnv = ElabEnv mempty | |||||
assume :: Name -> Value -> ElabM a -> ElabM a | |||||
assume nm ty = local go where | |||||
go = ElabEnv . Map.insert nm (ty, VVar nm) . getEnv | |||||
define :: Name -> Value -> Value -> ElabM a -> ElabM a | |||||
define nm ty vl = local go where | |||||
go = ElabEnv . Map.insert nm (ty, vl) . getEnv | |||||
getValue :: Name -> ElabM Value | |||||
getValue nm = do | |||||
vl <- asks (Map.lookup nm . getEnv) | |||||
case vl of | |||||
Just v -> pure (snd v) | |||||
Nothing -> liftIO . throwIO $ NotInScope nm | |||||
getNfType :: Name -> ElabM NFType | |||||
getNfType nm = do | |||||
vl <- asks (Map.lookup nm . getEnv) | |||||
case vl of | |||||
Just v -> pure (fst v) | |||||
Nothing -> liftIO . throwIO $ NotInScope nm |
@ -0,0 +1,96 @@ | |||||
{-# LANGUAGE PatternSynonyms #-} | |||||
module Syntax where | |||||
import Data.Function (on) | |||||
import Data.Text (Text) | |||||
import Presyntax.Presyntax (Plicity(..)) | |||||
import qualified Data.Text as T | |||||
import Data.IORef (IORef) | |||||
data Term | |||||
= Ref Name | |||||
| App Plicity Term Term | |||||
| Lam Plicity Text Term | |||||
| Pi Plicity Text Term Term | |||||
| Meta MV | |||||
| Type | |||||
| Sigma Text Term Term | |||||
| Pair Term Term | |||||
| Proj1 Term | |||||
| Proj2 Term | |||||
deriving (Eq, Show, Ord) | |||||
data MV = | |||||
MV { mvName :: Text | |||||
, mvCell :: {-# UNPACK #-} !(IORef (Maybe Value)) | |||||
} | |||||
instance Eq MV where | |||||
(==) = (==) `on` mvName | |||||
instance Ord MV where | |||||
(<=) = (<=) `on` mvName | |||||
instance Show MV where | |||||
show = ('?':) . T.unpack . mvName | |||||
data Name | |||||
= Bound Text | |||||
deriving (Eq, Show, Ord) | |||||
type NFType = Value | |||||
data Value | |||||
= VNe Head [Projection] | |||||
| VLam Plicity Closure | |||||
| VPi Plicity Value Closure | |||||
| VSigma Value Closure | |||||
| VPair Value Value | |||||
| VType | |||||
deriving (Eq, Show, Ord) | |||||
pattern VVar :: Name -> Value | |||||
pattern VVar x = VNe (HVar x) [] | |||||
quote :: Value -> Term | |||||
quote (VNe h sp) = foldl goSpine (goHead h) (reverse sp) where | |||||
goHead (HVar v) = Ref v | |||||
goHead (HMeta m) = Meta m | |||||
goSpine t (PApp p v) = App p t (quote v) | |||||
goSpine t PProj1 = Proj1 t | |||||
goSpine t PProj2 = Proj2 t | |||||
quote (VLam p (Closure n k)) = Lam p n (quote (k (VVar (Bound n)))) | |||||
quote (VPi p d (Closure n k)) = Pi p n (quote d) (quote (k (VVar (Bound n)))) | |||||
quote (VSigma d (Closure n k)) = Sigma n (quote d) (quote (k (VVar (Bound n)))) | |||||
quote (VPair a b) = Pair (quote a) (quote b) | |||||
quote VType = Type | |||||
data Closure | |||||
= Closure | |||||
{ clArgName :: Text | |||||
, clCont :: Value -> Value | |||||
} | |||||
instance Show Closure where | |||||
show (Closure n c) = "Closure \\" ++ show n ++ " -> " ++ show (c (VVar (Bound n))) | |||||
instance Eq Closure where | |||||
Closure _ k == Closure _ k' = | |||||
k (VVar (Bound (T.pack "_"))) == k' (VVar (Bound (T.pack "_"))) | |||||
instance Ord Closure where | |||||
Closure _ k <= Closure _ k' = | |||||
k (VVar (Bound (T.pack "_"))) <= k' (VVar (Bound (T.pack "_"))) | |||||
data Head | |||||
= HVar Name | |||||
| HMeta MV | |||||
deriving (Eq, Show, Ord) | |||||
data Projection | |||||
= PApp Plicity Value | |||||
| PProj1 | |||||
| PProj2 | |||||
deriving (Eq, Show, Ord) |