{-# LANGUAGE LambdaCase #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE ScopedTypeVariables #-} module Elab.Eval where import Control.Monad.Reader import Control.Exception import qualified Data.Map.Strict as Map import qualified Data.Sequence as Seq import qualified Data.Set as Set import qualified Data.Text as T import Data.Sequence (Seq) 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.Pretty import Syntax import System.IO.Unsafe import {-# SOURCE #-} Elab.WiredIn import Prettyprinter eval :: Term -> ElabM Value eval t = asks (flip eval' 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 args Nothing -> pure vl forceIO x = pure x applProj :: Value -> Projection -> Value applProj fun (PApp p arg) = vApp p fun arg applProj fun (PIElim l x y i) = ielim l x y fun i applProj fun PProj1 = vProj1 fun applProj fun PProj2 = vProj2 fun force :: Value -> Value force = unsafePerformIO . forceIO -- everywhere force zonkIO :: Value -> IO Value zonkIO (VNe hd sp) = do sp' <- traverse zonkSp sp case hd of HMeta (MV _ cell) -> do solved <- liftIO $ readIORef cell case solved of Just vl -> zonkIO $ foldl applProj vl sp' Nothing -> pure $ VNe hd sp' hd -> pure $ VNe hd sp' where zonkSp (PApp p x) = PApp p <$> zonkIO x zonkSp (PIElim l x y i) = PIElim <$> zonkIO l <*> zonkIO x <*> zonkIO y <*> zonkIO i zonkSp PProj1 = pure PProj1 zonkSp PProj2 = pure PProj2 zonkIO (VLam p (Closure s k)) = pure $ VLam p (Closure s (zonk . k)) zonkIO (VPi p d (Closure s k)) = VPi p <$> zonkIO d <*> pure (Closure s (zonk . k)) zonkIO (VSigma d (Closure s k)) = VSigma <$> zonkIO d <*> pure (Closure s (zonk . k)) zonkIO (VPair a b) = VPair <$> zonkIO a <*> zonkIO b zonkIO (VPath line x y) = VPath <$> zonkIO line <*> zonkIO x <*> zonkIO y zonkIO (VLine line f) = VLine <$> zonkIO line <*> zonkIO f -- Sorts zonkIO VType = pure VType zonkIO VTypeω = pure VTypeω zonkIO VI = pure VI zonkIO VI0 = pure VI0 zonkIO VI1 = pure VI1 zonkIO (VIAnd x y) = iand <$> zonkIO x <*> zonkIO y zonkIO (VIOr x y) = ior <$> zonkIO x <*> zonkIO y zonkIO (VINot x) = inot <$> zonkIO x zonk :: Value -> Value zonk = unsafePerformIO . zonkIO eval' :: ElabEnv -> Term -> Value eval' env (Ref x) = case Map.lookup x (getEnv env) of Just (_, vl) -> vl _ -> error "variable not in scope when evaluating" eval' env (App p f x) = vApp p (eval' env f) (eval' env x) eval' env (Lam p s t) = VLam p $ Closure s $ \a -> eval' env { getEnv = Map.insert (Bound s) (error "type of abs", a) (getEnv env) } t eval' env (Pi p s d t) = VPi p (eval' env d) $ Closure s $ \a -> eval' env { getEnv = (Map.insert (Bound s) (error "type of abs", a) (getEnv env))} t eval' _ (Meta m) = VNe (HMeta m) mempty eval' env (Sigma s d t) = VSigma (eval' env d) $ Closure s $ \a -> eval' env { getEnv = Map.insert (Bound s) (error "type of abs", a) (getEnv env) } t eval' e (Pair a b) = VPair (eval' e a) (eval' e b) eval' e (Proj1 a) = vProj1 (eval' e a) eval' e (Proj2 a) = vProj2 (eval' e a) eval' _ Type = VType eval' _ Typeω = VTypeω eval' _ I = VI eval' _ I0 = VI0 eval' _ I1 = VI1 eval' e (IAnd x y) = iand (eval' e x) (eval' e y) eval' e (IOr x y) = ior (eval' e x) (eval' e y) eval' e (INot x) = inot (eval' e x) eval' e (PathP l a b) = VPath (eval' e l) (eval' e a) (eval' e b) eval' e (IElim l x y f i) = ielim (eval' e l) (eval' e x) (eval' e y) (eval' e f) (eval' e i) eval' e (PathIntro p f) = VLine (eval' e p) (eval' e f) vApp :: Plicity -> Value -> Value -> Value vApp p (VLam p' k) arg | p == p' = clCont k arg | otherwise = error $ "wrong plicity " ++ show p ++ " vs " ++ show p' ++ " in app " ++ show (App p (quote (VLam p' k)) (quote arg)) vApp p (VNe h sp) arg = VNe h (sp Seq.:|> PApp p arg) vApp _ x _ = error $ "can't apply " ++ show x (@@) :: Value -> Value -> Value (@@) = vApp Ex infixl 9 @@ vProj1 :: Value -> Value vProj1 (VPair a _) = a vProj1 (VNe h sp) = VNe h (sp Seq.:|> PProj1) vProj1 x = error $ "can't proj1 " ++ show x vProj2 :: Value -> Value vProj2 (VPair _ b) = b vProj2 (VNe h sp) = VNe h (sp Seq.:|> PProj2) 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 rhs (VNe (HMeta mv) sp) = solveMeta mv sp rhs go (VNe x a) (VNe x' a') | x == x', length a == length a' = traverse_ (uncurry unify'Spine) (Seq.zip a a') go (VNe _hd (_ Seq.:|> PIElim _l x y i)) rhs = case force i of VI0 -> unify' x rhs VI1 -> unify' y rhs _ -> fail go (VLam p (Closure _ k)) vl = do t <- VVar . Bound <$> newName unify' (k t) (vApp p vl t) go vl (VLam p (Closure _ k)) = do t <- VVar . Bound <$> newName unify' (vApp p vl t) (k t) go (VPair a b) vl = unify' a (vProj1 vl) *> unify' b (vProj2 vl) go vl (VPair a b) = unify' (vProj1 vl) a *> unify' (vProj2 vl) b 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 (VSigma d (Closure _ k)) (VSigma d' (Closure _ k')) = do t <- VVar . Bound <$> newName unify' d d' unify' (k t) (k' t) go VType VType = pure () go VTypeω VTypeω = pure () go VI VI = pure () go VI0 VI0 = pure () go VI1 VI1 = pure () go (VINot x) (VINot y) = unify' x y go (VIAnd x y) (VIAnd x' y') = unify' x x' *> unify' y y' go (VIOr x y) (VIOr x' y') = unify' x x' *> unify' y y' go (VPath l x y) (VPath l' x' y') = do unify' l l' unify' x x' unify' y y' go (VLine l p) p' = do n <- VVar . Bound <$> newName unify (p @@ n) (ielim l (l @@ VI0) (l @@ VI1) p' n) go p' (VLine l p) = do n <- VVar . Bound <$> newName unify (ielim l (l @@ VI0) (l @@ VI1) p' n) (p @@ n) go _ _ = fail fail = liftIO . throwIO $ NotEqual topa topb unify'Spine (PApp a v) (PApp a' v') | a == a' = unify' v v' unify'Spine PProj1 PProj1 = pure () unify'Spine PProj2 PProj2 = pure () unify'Spine (PIElim _ _ _ i) (PIElim _ _ _ j) = unify' i j unify'Spine _ _ = fail unify :: Value -> Value -> ElabM () unify a b = unify' a b `catchElab` \(_ :: SomeException) -> liftIO $ throwIO (NotEqual a b) 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 Im f (quote meta))) VType `isConvertibleTo` VTypeω = pure id VPi p d (Closure _ k) `isConvertibleTo` VPi p' d' (Closure _ k') | p == p' = do wp <- d' `isConvertibleTo` d n <- newName wp_n <- eval (Lam Ex n (wp (Ref (Bound n)))) wp' <- k (VVar (Bound n)) `isConvertibleTo` k' (wp_n @@ VVar (Bound n)) pure (\f -> Lam p n (wp' (App p f (wp (Ref (Bound n)))))) 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, _) -> pure $ case n of Bound{} -> Just (PApp Ex (VVar n)) _ -> Nothing pure (VNe (HMeta m) (Seq.fromList (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 -> Seq Projection -> Value -> ElabM () solveMeta m@(MV _ cell) sp rhs = do env <- ask names <- checkSpine Set.empty sp checkScope (Set.fromList (Bound <$> names)) rhs `withNote` hsep [prettyTm (quote (VNe (HMeta m) sp)), pretty '≡', prettyTm (quote rhs)] let tm = quote rhs lam = eval' env $ 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@Bound{} -> unless (v `Set.member` scope) . liftIO . throwIO $ NotInScope v HVar{} -> pure () HMeta{} -> pure () traverse_ checkProj sp where checkProj (PApp _ t) = checkScope scope t checkProj (PIElim l x y i) = traverse_ (checkScope scope) [l, x, y, i] 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 () checkScope _ VTypeω = pure () checkScope _ VI = pure () checkScope _ VI0 = pure () checkScope _ VI1 = pure () checkScope s (VIAnd x y) = traverse_ (checkScope s) [x, y] checkScope s (VIOr x y) = traverse_ (checkScope s) [x, y] checkScope s (VINot x) = checkScope s x checkScope s (VPath line a b) = traverse_ (checkScope s) [line, a, b] checkScope s (VLine _ line) = checkScope s line checkSpine :: Set Name -> Seq Projection -> ElabM [T.Text] checkSpine scope (PApp Ex (VVar n@(Bound t)) Seq.:<| xs) | n `Set.member` scope = liftIO . throwIO $ NonLinearSpine n | otherwise = (t:) <$> checkSpine scope xs checkSpine _ (p Seq.:<| _) = liftIO . throwIO $ SpineProj p checkSpine _ Seq.Empty = pure [] newtype NonLinearSpine = NonLinearSpine { getDupeName :: Name } deriving (Show, Typeable, Exception) newtype SpineProjection = SpineProj { getSpineProjection :: Projection } deriving (Show, Typeable, Exception)