diff --git a/app/Main.hs b/app/Main.hs index 042a9ce..670646e 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -114,8 +114,8 @@ commandParser = do class MonadState AppState m => MonadApp m where parsed :: Either ParseError a -> m a - typecheckDecl :: Text -> CheckExpr -> m (Maybe Scheme) - typecheckExpr :: CheckExpr -> m (Maybe Scheme) + typecheckDecl :: Maybe Type -> Text -> CheckExpr -> m (Maybe Scheme) + typecheckExpr :: CheckExpr -> m (Maybe Scheme) execute :: CheckExpr -> m EvalExpr type AppM = ExceptT Text (StateT AppState (InputT IO)) @@ -127,8 +127,8 @@ instance MonadApp AppM where parsed (Left err) = throwError $ T.pack $ show err parsed (Right ok) = pure ok - typecheckDecl = typecheck . Just - typecheckExpr = typecheck Nothing + typecheckDecl ty = typecheck ty . Just + typecheckExpr = typecheck Nothing Nothing execute checkExpr = do defs <- gets definitions @@ -148,10 +148,10 @@ instance MonadApp AppM where liftInput $ mapM_ (outputTextLn . unparseEval) trace pure value -typecheck :: Maybe Text -> CheckExpr -> AppM (Maybe Scheme) -typecheck decl expr = do +typecheck :: Maybe Type -> Maybe Text -> CheckExpr -> AppM (Maybe Scheme) +typecheck tann decl expr = do defs <- gets definitions - let type_ = infer $ substitute defs expr + let type_ = maybe infer check tann $ substitute defs expr checkOpts <- gets checkOptions if shouldTypecheck checkOpts then case type_ of @@ -184,10 +184,10 @@ define name expr = modify \appState -> in appState { definitions = HM.insert name expr' $ definitions appState } runDeclOrExpr :: MonadApp m => DeclOrExprAST -> m () -runDeclOrExpr (Left (name, exprAST)) = do +runDeclOrExpr (Left (name, ty, exprAST)) = do defs <- gets definitions let expr = substitute defs $ ast2check exprAST - _ <- typecheckDecl name expr + _ <- typecheckDecl ty name expr define name expr runDeclOrExpr (Right exprAST) = do defs <- gets definitions diff --git a/package.yaml b/package.yaml index 9569ebd..6f6cf8c 100644 --- a/package.yaml +++ b/package.yaml @@ -14,6 +14,7 @@ extra-source-files: default-extensions: - BlockArguments +- ConstraintKinds - DefaultSignatures - EmptyCase - EmptyDataDeriving diff --git a/src/Ivo/Evaluator.hs b/src/Ivo/Evaluator.hs index 54367ec..c402ecb 100644 --- a/src/Ivo/Evaluator.hs +++ b/src/Ivo/Evaluator.hs @@ -14,7 +14,7 @@ import Control.Monad.Loops (iterateM_) import Control.Monad.State (MonadState, evalState, modify', state, put, gets) import Control.Monad.Writer (runWriterT, tell) import Data.HashMap.Strict qualified as HM -import Data.Void (Void, absurd) +import Data.Void (absurd) isReducible :: EvalExpr -> Bool -- Applications of function type constructors diff --git a/src/Ivo/Evaluator/Base.hs b/src/Ivo/Evaluator/Base.hs index d814d63..969f056 100644 --- a/src/Ivo/Evaluator/Base.hs +++ b/src/Ivo/Evaluator/Base.hs @@ -1,5 +1,5 @@ module Ivo.Evaluator.Base - ( Identity (..) + ( Identity (..), Void , Expr (..), Ctr (..), Pat, ExprF (..), PatF (..), VoidF, UnitF (..), Text , substitute, substitute1, rename, rename1, free, bound, used , Eval, EvalExpr, EvalExprF, EvalX, EvalXF (..) @@ -17,6 +17,7 @@ import Data.Functor.Identity (Identity (..)) import Data.Functor.Foldable (embed, cata, para) import Data.HashMap.Strict qualified as HM import Data.Traversable (for) +import Data.Void (Void) data Eval type EvalExpr = Expr Eval @@ -24,6 +25,7 @@ type instance AppArgs Eval = EvalExpr type instance AbsArgs Eval = Text type instance LetArgs Eval = VoidF EvalExpr type instance CtrArgs Eval = UnitF EvalExpr +type instance AnnX Eval = Void type instance XExpr Eval = EvalX type EvalX = EvalXF EvalExpr diff --git a/src/Ivo/Expression.hs b/src/Ivo/Expression.hs index 08c3baf..f924cc7 100644 --- a/src/Ivo/Expression.hs +++ b/src/Ivo/Expression.hs @@ -1,5 +1,6 @@ module Ivo.Expression ( Expr (..), Ctr (..), Pat, ExprF (..), PatF (..), DefF (..), VoidF, UnitF (..), Text + , Type (..), TypeF (..), Scheme (..), tapp , substitute, substitute1, rename, free, bound, used , Eval, EvalExpr, EvalX, EvalXF (..), Identity (..) , pattern AppFE, pattern CtrE, pattern CtrFE, @@ -10,7 +11,6 @@ module Ivo.Expression , Check, CheckExpr, CheckExprF, CheckX, CheckXF (..) , pattern AppFC, pattern CtrC, pattern CtrFC, pattern CallCCC, pattern CallCCFC , pattern FixC, pattern FixFC, pattern HoleC, pattern HoleFC - , Type (..), TypeF (..), Scheme (..), tapp , ast2check, ast2eval, check2eval, check2ast, eval2ast , builtins ) where @@ -41,6 +41,7 @@ ast2check = substitute builtins . cata \case LetRecFP (nx, ex) e -> App (Abs nx e) (App FixC (Abs nx ex)) CtrF ctr es -> foldl' App (CtrC ctr) es CaseF ps -> Case ps + AnnF () e t -> Ann () e t PNatF n -> int2ast n PListF es -> mkList es PStrF s -> mkList $ map (App (CtrC CChar) . int2ast . fromEnum) $ unpack s @@ -63,6 +64,7 @@ check2eval = cata \case LetF (Def nx ex) e -> App (Abs nx e) ex CtrFC ctr -> CtrE ctr CaseF ps -> Case ps + AnnF () e _ -> e CallCCFC -> CallCCE FixFC -> z HoleFC -> omega @@ -88,6 +90,7 @@ check2ast = hoist go . rename (HM.keysSet builtins) LetF (Def nx ex) e -> LetFP ((nx, ex) :| []) e CtrFC ctr -> CtrF ctr [] CaseF ps -> CaseF ps + AnnF () e t -> AnnF () e t CallCCFC-> VarF "callcc" FixFC -> VarF "fix" HoleFC -> HoleFP diff --git a/src/Ivo/Expression/Base.hs b/src/Ivo/Expression/Base.hs index 34f1cb2..148db23 100644 --- a/src/Ivo/Expression/Base.hs +++ b/src/Ivo/Expression/Base.hs @@ -1,9 +1,11 @@ -{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE UndecidableSuperClasses #-} module Ivo.Expression.Base ( Text, VoidF, UnitF (..), absurd' - , Expr (..), Ctr (..), Pat, Def, AppArgs, AbsArgs, LetArgs, CtrArgs, XExpr + , Expr (..), Ctr (..), Pat, Def, AppArgs, AbsArgs, LetArgs, CtrArgs, AnnX, XExpr , ExprF (..), PatF (..), DefF (..), AppArgsF, LetArgsF, CtrArgsF, XExprF + , Type (..), TypeF (..), Scheme (..), tapp , RecursivePhase, projectAppArgs, projectLetArgs, projectCtrArgs, projectXExpr, projectDef , embedAppArgs, embedLetArgs, embedCtrArgs, embedXExpr, embedDef , Substitutable, free, bound, used, collectVars, rename, rename1 @@ -15,30 +17,34 @@ import Control.Monad.Reader (MonadReader, Reader, runReader, asks, local) import Control.Monad.State (MonadState, StateT, evalStateT, state) import Control.Monad.Zip (MonadZip, mzipWith) import Data.Foldable (fold) -import Data.Functor.Foldable (Base, Recursive, Corecursive, project, embed) +import Data.Functor.Foldable (Base, Recursive, Corecursive, project, embed, cata) +import Data.Functor.Foldable.TH (makeBaseFunctor) import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict qualified as HM import Data.HashSet (HashSet) import Data.HashSet qualified as HS -import Data.Kind (Type) +import Data.Kind qualified as Kind +import Data.List (foldl1') import Data.Stream qualified as S import Data.Text (Text) import Data.Text qualified as T data Expr phase - -- | A variable: `x`. + -- | A variable: @x@. = Var !Text - -- | Function application: `f x_0 ... x_n`. + -- | Function application: @f x_0 ... x_n@. | App !(Expr phase) !(AppArgs phase) - -- | Lambda abstraction: `λx_0 ... x_n. e`. + -- | Lambda abstraction: @λx_0 ... x_n. e@. | Abs !(AbsArgs phase) !(Expr phase) - -- | Let expression: `let x_0 = v_0 ... ; x_n = v_n in e`. + -- | Let expression: @let x_0 = v_0 ... ; x_n = v_n in e@. | Let !(LetArgs phase) !(Expr phase) - -- | Data constructor, e.g. `(x, y)` or `Left`. + -- | Data constructor, e.g. @(x, y)@ or @Left@. | Ctr !Ctr !(CtrArgs phase) -- | Case expression to pattern match against a value, - -- e.g. `case { Left x1 -> e1 ; Right x2 -> e2 }`. + -- e.g. @case { Left x1 -> e1 ; Right x2 -> e2 }@. | Case ![Pat phase] + -- | Type annotations: @expr : type@. + | Ann !(AnnX phase) !(Expr phase) Type -- | Additional phase-specific constructors. | ExprX !(XExpr phase) @@ -46,23 +52,30 @@ type family AppArgs phase type family AbsArgs phase type family LetArgs phase type family CtrArgs phase +type family AnnX phase type family XExpr phase -deriving instance - ( Eq (AppArgs phase) - , Eq (AbsArgs phase) - , Eq (LetArgs phase) - , Eq (CtrArgs phase) - , Eq (XExpr phase) - ) => Eq (Expr phase) +class + ( c (AppArgs phase) + , c (AbsArgs phase) + , c (LetArgs phase) + , c (CtrArgs phase) + , c (AnnX phase) + , c (XExpr phase) + ) => ForallX c phase -deriving instance - ( Show (AppArgs phase) - , Show (AbsArgs phase) - , Show (LetArgs phase) - , Show (CtrArgs phase) - , Show (XExpr phase) - ) => Show (Expr phase) +instance + ( c (AppArgs phase) + , c (AbsArgs phase) + , c (LetArgs phase) + , c (CtrArgs phase) + , c (AnnX phase) + , c (XExpr phase) + ) => ForallX c phase + +deriving instance ForallX Eq phase => Eq (Expr phase) + +deriving instance ForallX Show phase => Show (Expr phase) -- | Data constructors (used in pattern matching and literals). data Ctr @@ -94,6 +107,41 @@ data PatF r = Pat { patCtr :: !Ctr, patNames :: ![Text], patBody :: !r } -- | A definition, mapping a name to a value. type Def phase = (Text, Expr phase) +-- | A monomorphic type. +data Type + -- | Type variable. + = TVar Text + -- | Type application. + | TApp Type Type + -- | The function type. + | TAbs + -- | The product type. + | TProd + -- | The sum type. + | TSum + -- | The unit type. + | TUnit + -- | The empty type. + | TVoid + -- | The type of natural numbers. + | TNat + -- | The type of lists. + | TList + -- | The type of characters. + | TChar + deriving (Eq, Show) + +tapp :: [Type] -> Type +tapp [] = error "Empty type applications are not permitted" +tapp [t] = t +tapp ts = foldl1' TApp ts + +-- | A polymorphic type. +data Scheme + -- | Universally quantified type variables. + = TForall [Text] Type + deriving (Eq, Show) + --- --- Base functor boilerplate for recursion-schemes --- @@ -105,14 +153,29 @@ data ExprF phase r | LetF !(LetArgsF phase r) r | CtrF Ctr (CtrArgsF phase r) | CaseF [PatF r] + | AnnF !(AnnX phase) r Type | ExprXF !(XExprF phase r) type instance Base (Expr phase) = ExprF phase -type family AppArgsF phase :: Type -> Type -type family LetArgsF phase :: Type -> Type -type family CtrArgsF phase :: Type -> Type -type family XExprF phase :: Type -> Type +type family AppArgsF phase :: Kind.Type -> Kind.Type +type family LetArgsF phase :: Kind.Type -> Kind.Type +type family CtrArgsF phase :: Kind.Type -> Kind.Type +type family XExprF phase :: Kind.Type -> Kind.Type + +class + ( c (AppArgsF phase) + , c (LetArgsF phase) + , c (CtrArgsF phase) + , c (XExprF phase) + ) => ForallXF c phase + +instance + ( c (AppArgsF phase) + , c (LetArgsF phase) + , c (CtrArgsF phase) + , c (XExprF phase) + ) => ForallXF c phase data DefF r = Def !Text !r deriving (Eq, Functor, Foldable, Traversable, Show) @@ -128,12 +191,7 @@ data VoidF a absurd' :: VoidF a -> b absurd' x = case x of {} -instance - ( Functor (AppArgsF phase) - , Functor (LetArgsF phase) - , Functor (CtrArgsF phase) - , Functor (XExprF phase) - ) => Functor (ExprF phase) where +instance ForallXF Functor phase => Functor (ExprF phase) where fmap f = \case VarF n -> VarF n AppF ef exs -> AppF (f ef) (fmap f exs) @@ -141,14 +199,10 @@ instance LetF ds e -> LetF (fmap f ds) (f e) CtrF c es -> CtrF c (fmap f es) CaseF ps -> CaseF (fmap (fmap f) ps) + AnnF x e t -> AnnF x (f e) t ExprXF q -> ExprXF (fmap f q) -instance - ( Foldable (AppArgsF phase) - , Foldable (LetArgsF phase) - , Foldable (CtrArgsF phase) - , Foldable (XExprF phase) - ) => Foldable (ExprF phase) where +instance ForallXF Foldable phase => Foldable (ExprF phase) where foldMap f = \case VarF _ -> mempty AppF ef exs -> f ef <> foldMap f exs @@ -156,14 +210,10 @@ instance LetF ds e -> foldMap f ds <> f e CtrF _ es -> foldMap f es CaseF ps -> foldMap (foldMap f) ps + AnnF _ e _ -> f e ExprXF q -> foldMap f q -instance - ( Traversable (AppArgsF phase) - , Traversable (LetArgsF phase) - , Traversable (CtrArgsF phase) - , Traversable (XExprF phase) - ) => Traversable (ExprF phase) where +instance ForallXF Traversable phase => Traversable (ExprF phase) where traverse f = \case VarF n -> pure $ VarF n AppF ef exs -> AppF <$> f ef <*> traverse f exs @@ -171,6 +221,7 @@ instance LetF ds e -> LetF <$> traverse f ds <*> f e CtrF c es -> CtrF c <$> traverse f es CaseF ps -> CaseF <$> traverse (traverse f) ps + AnnF x e t -> (\e' -> AnnF x e' t) <$> f e ExprXF q -> ExprXF <$> traverse f q class Functor (ExprF phase) => RecursivePhase phase where @@ -226,6 +277,7 @@ instance RecursivePhase phase => Recursive (Expr phase) where Let ds e -> LetF (projectLetArgs ds) e Ctr c es -> CtrF c (projectCtrArgs es) Case ps -> CaseF ps + Ann x e t -> AnnF x e t ExprX q -> ExprXF (projectXExpr q) instance RecursivePhase phase => Corecursive (Expr phase) where @@ -236,8 +288,11 @@ instance RecursivePhase phase => Corecursive (Expr phase) where LetF ds e -> Let (embedLetArgs ds) e CtrF c es -> Ctr c (embedCtrArgs es) CaseF ps -> Case ps + AnnF x e t -> Ann x e t ExprXF q -> ExprX (embedXExpr q) +makeBaseFunctor ''Type + --- --- End base functor boilerplate. --- @@ -283,6 +338,34 @@ class Substitutable e where unsafeSubstitute1 :: Text -> e -> e -> e unsafeSubstitute1 n e = unsafeSubstitute (HM.singleton n e) +instance Substitutable Type where + collectVars withVar _ = cata \case + TVarF n -> withVar n + t -> fold t + + -- /All/ variables in a monomorphic type are free. + rename _ t = t + + -- No renaming step is necessary. + substitute substs = cata \case + TVarF n -> HM.findWithDefault (TVar n) n substs + e -> embed e + + unsafeSubstitute = substitute + +instance Substitutable Scheme where + collectVars withVar withBinder (TForall names t) = + foldMap withBinder names $ collectVars withVar withBinder t + + rename = runRenamer \badNames (TForall names t) -> + uncurry TForall <$> replaceNames badNames names (pure t) + + -- I took a shot at implementing this but found it to be quite difficult + -- because merging the foralls is tricky. + -- It's not undoable, but it wasn't worth my further time investment + -- seeing as this function isn't currently used anywhere. + unsafeSubstitute = error "Substitution for schemes not yet implemented" + -- -- These primitives are likely to be useful for implementing `rename`. -- Ideally, I would like to find a way to move the implementation of `rename` here entirely, diff --git a/src/Ivo/Syntax/Base.hs b/src/Ivo/Syntax/Base.hs index 011edbd..b5f0198 100644 --- a/src/Ivo/Syntax/Base.hs +++ b/src/Ivo/Syntax/Base.hs @@ -1,5 +1,6 @@ module Ivo.Syntax.Base ( Expr (..), ExprF (..), Ctr (..), Pat, Def, DefF (..), PatF (..), VoidF, Text, NonEmpty (..) + , Type (..), TypeF (..), Scheme (..), tapp , substitute, substitute1, rename, rename1, free, bound, used , Parse, AST, ASTF, ASTX, ASTXF (..), NonEmptyDefFs (..) , pattern LetFP, pattern LetRecP, pattern LetRecFP @@ -36,6 +37,7 @@ type instance AppArgs Parse = NonEmpty AST type instance AbsArgs Parse = NonEmpty Text type instance LetArgs Parse = NonEmpty (Def Parse) type instance CtrArgs Parse = [AST] +type instance AnnX Parse = () type instance XExpr Parse = ASTX type ASTX = ASTXF AST @@ -107,10 +109,10 @@ pattern HoleP = ExprX HoleP_ pattern HoleFP :: ASTF r pattern HoleFP = ExprXF HoleP_ -{-# COMPLETE VarF, AppF, AbsF, LetFP, CtrF, CaseF, ExprXF #-} -{-# COMPLETE Var, App, Abs, Let, Ctr, Case, LetRecP, PNat, PList, PChar, PStr, HoleP #-} -{-# COMPLETE VarF, AppF, AbsF, LetF , CtrF, CaseF, LetRecFP, PNatF, PListF, PCharF, PStrF, HoleFP #-} -{-# COMPLETE VarF, AppF, AbsF, LetFP, CtrF, CaseF, LetRecFP, PNatF, PListF, PCharF, PStrF, HoleFP #-} +{-# COMPLETE VarF, AppF, AbsF, LetFP, CtrF, CaseF, AnnF, ExprXF #-} +{-# COMPLETE Var, App, Abs, Let, Ctr, Case, Ann, LetRecP, PNat, PList, PChar, PStr, HoleP #-} +{-# COMPLETE VarF, AppF, AbsF, LetF , CtrF, CaseF, AnnF, LetRecFP, PNatF, PListF, PCharF, PStrF, HoleFP #-} +{-# COMPLETE VarF, AppF, AbsF, LetFP, CtrF, CaseF, AnnF, LetRecFP, PNatF, PListF, PCharF, PStrF, HoleFP #-} -- TODO: Implement Substitutable for AST. diff --git a/src/Ivo/Syntax/Parser.hs b/src/Ivo/Syntax/Parser.hs index 1be06c1..9727bfa 100644 --- a/src/Ivo/Syntax/Parser.hs +++ b/src/Ivo/Syntax/Parser.hs @@ -1,17 +1,19 @@ module Ivo.Syntax.Parser ( ParseError, parse - , DeclOrExprAST, ProgramAST + , Declaration, DeclOrExprAST, ProgramAST , parseAST, parseDeclOrExpr, parseProgram - , astParser, declOrExprParser, programParser + , typeParser, schemeParser, astParser, declOrExprParser, programParser ) where import Ivo.Syntax.Base +import Data.Functor.Identity (Identity) import Data.List.NonEmpty (fromList) import Data.Text qualified as T import Prelude hiding (succ, either) import Text.Parsec hiding (label, token, spaces) import Text.Parsec qualified +import Text.Parsec.Expr import Text.Parsec.Text (Parser) spaces :: Parser () @@ -36,7 +38,7 @@ token :: Char -> Parser () token ch = label [ch] $ char ch *> spaces keywords :: [Text] -keywords = ["let", "in", "Left", "Right", "S", "Z", "Char"] +keywords = ["let", "in", "Left", "Right", "S", "Z", "forall", "Char", "Void", "Unit", "Nat", "Char"] -- | A keyword is an exact string which is not part of an identifier. keyword :: Text -> Parser () @@ -56,6 +58,9 @@ identifier = label "identifier" $ do variable :: Parser AST variable = label "variable" $ Var <$> identifier +tvariable :: Parser Type +tvariable = label "variable" $ TVar <$> identifier + many1' :: Parser a -> Parser (NonEmpty a) many1' p = fromList <$> many1 p @@ -65,22 +70,29 @@ many2 p = (,) <$> p <*> many1' p grouping :: Parser AST grouping = label "grouping" $ between (token '(') (token ')') ambiguous +tgrouping :: Parser Type +tgrouping = label "grouping" $ between (token '(') (token ')') tambiguous + application :: Parser AST -application = uncurry App <$> many2 block +application = label "application" $ uncurry App <$> many2 block + +tapplication :: Parser Type +tapplication = label "application" $ uncurry tapp' <$> many2 tblock + where tapp' t1 (t2 :| ts) = tapp (t1 : t2 : ts) abstraction :: Parser AST abstraction = label "lambda abstraction" $ Abs <$> between lambda (token '.') (many1' identifier) <*> ambiguous where lambda = label "lambda" $ (char '\\' <|> char 'λ') *> spaces definition :: Parser (Def Parse) -definition = do +definition = label "definition" $ do name <- identifier token '=' value <- ambiguous pure (name, value) let_ :: Parser AST -let_ = letrecstar <|> letstar +let_ = label "let expression" $ letrecstar <|> letstar where letrecstar = LetRecP <$> between (try (keyword "letrec")) (keyword "in") definition <*> ambiguous letstar = Let <$> between (keyword "let") (keyword "in") definitions <*> ambiguous @@ -89,7 +101,7 @@ let_ = letrecstar <|> letstar definitions = fromList <$> sepBy1 definition (token ';') ctr :: Parser AST -ctr = pair <|> unit <|> either <|> nat <|> list <|> str +ctr = label "data constructor" $ pair <|> unit <|> either <|> nat <|> list <|> str where unit, pairCtr, tuple, either, left, right, zero, succ, natLit, consCtr, cons, charCtr, charLit, strLit :: Parser AST @@ -166,9 +178,62 @@ case_ = label "case patterns" $ do token '}' pure $ Case pats +ann :: Parser AST +ann = label "type annotation" $ do + e <- block + token ':' + t <- tambiguous + pure (Ann () e t) + hole :: Parser AST hole = label "hole" $ HoleP <$ token '_' +tlist :: Parser Type +tlist = between (token '[') (token ']') $ ((TApp TList <$> tambiguous) <|> pure TList) + +tinfix :: Parser Type +tinfix = buildExpressionParser ttable tblock + where + ttable :: [[Operator Text () Identity Type]] + ttable = [ [Infix (binop TAbs <$ arrSym) AssocRight] + , [Infix (binop TProd <$ token '*') AssocRight] + , [Infix (binop TSum <$ token '+') AssocRight] + ] + + arrSym :: Parser () + arrSym = token '→' <|> keyword "->" + + binop :: Type -> Type -> Type -> Type + binop c t1 t2 = TApp (TApp c t1) t2 + +tctr :: Parser Type +tctr = tlist <|> tunit <|> tvoid <|> tnat <|> tchar + where + tunit = TUnit <$ (keyword "Unit" <|> keyword "⊤") + tvoid = TVoid <$ (keyword "Void" <|> keyword "⊥") + tnat = TNat <$ (keyword "Nat" <|> keyword "ℕ") + tchar = TChar <$ keyword "Char" + +tfinite :: Parser Type +tfinite = tvariable <|> tlist <|> tctr <|> tgrouping + +tblock :: Parser Type +tblock = tfinite + +tambiguous :: Parser Type +tambiguous = try tinfix <|> try tapplication <|> tblock + +tforall :: Parser Scheme +tforall = do + keyword "forall" <|> token '∀' + names <- many1 (identifier <* spaces) + token '.' + ty <- tambiguous + pure $ TForall names ty + +scheme :: Parser Scheme +scheme = tforall <|> (TForall [] <$> tambiguous) + -- | Guaranteed to consume a finite amount of input finite :: Parser AST finite = label "finite expression" $ variable <|> hole <|> ctr <|> case_ <|> grouping @@ -179,7 +244,13 @@ block = label "block expression" $ abstraction <|> let_ <|> finite -- | Not guaranteed to consume input at all, may continue until it reaches a terminator ambiguous :: Parser AST -ambiguous = label "any expression" $ try application <|> block +ambiguous = label "any expression" $ try ann <|> try application <|> block + +typeParser :: Parser Type +typeParser = tambiguous + +schemeParser :: Parser Scheme +schemeParser = scheme astParser :: Parser AST astParser = ambiguous @@ -187,18 +258,26 @@ astParser = ambiguous parseAST :: Text -> Either ParseError AST parseAST = parse (spaces *> ambiguous <* eof) "input" -type Declaration = (Text, AST) +type Declaration = (Text, Maybe Type, AST) + +definitionAnn :: Parser Declaration +definitionAnn = do + name <- identifier + ty <- optionMaybe $ token ':' *> tambiguous + token '=' + e <- ambiguous + pure (name, ty, e) declaration :: Parser Declaration declaration = notFollowedBy (try let_) >> (declrec <|> decl) where declrec = do try $ keyword "letrec" - (name, expr) <- definition - pure (name, LetRecP (name, expr) (Var name)) + (name, ty, expr) <- definitionAnn + pure (name, ty, LetRecP (name, expr) (Var name)) decl = do keyword "let" - definition + definitionAnn -- | A program is a series of declarations and expressions to execute. type ProgramAST = [DeclOrExprAST] diff --git a/src/Ivo/Syntax/Printer.hs b/src/Ivo/Syntax/Printer.hs index 850a2c2..3cb3b91 100644 --- a/src/Ivo/Syntax/Printer.hs +++ b/src/Ivo/Syntax/Printer.hs @@ -1,10 +1,11 @@ -module Ivo.Syntax.Printer (unparseAST) where +module Ivo.Syntax.Printer (unparseAST, unparseType, unparseScheme) where import Ivo.Syntax.Base import Data.Functor.Base (NonEmptyF (NonEmptyF)) import Data.Functor.Foldable (cata) import Data.List.NonEmpty (toList) +import Data.Text qualified as T import Data.Text.Lazy (fromStrict, toStrict, intercalate, unwords, singleton) import Data.Text.Lazy.Builder (Builder, fromText, fromLazyText, toLazyText, fromString) import Prelude hiding (unwords) @@ -62,6 +63,7 @@ unparseAST = toStrict . toLazyText . snd . cata \case CaseF pats -> let pats' = fromLazyText $ intercalate "; " $ map (toLazyText . unparsePat) pats in tag Finite $ "{ " <> pats' <> " }" + AnnF () e t -> tag Ambiguous $ final e <> " : " <> fromText (unparseType t) PNatF n -> tag Finite $ fromString $ show n PListF es -> let es' = fromLazyText $ intercalate ", " $ map (toLazyText . unambiguous) es @@ -95,3 +97,28 @@ unparseAST = toStrict . toLazyText . snd . cata \case unparsePat (Pat ctr ns e) = unambiguous (unparseCtr ctr (map (tag Finite . fromText) ns)) <> " -> " <> unambiguous e + +-- HACK +pattern TApp2 :: Type -> Type -> Type -> Type +pattern TApp2 tf tx ty = TApp (TApp tf tx) ty + +-- TODO: Improve these printers. +unparseType :: Type -> Text +unparseType (TVar name) = name +unparseType (TApp2 TAbs a b) = "(" <> unparseType a <> " -> " <> unparseType b <> ")" +unparseType (TApp2 TProd a b) = "(" <> unparseType a <> " * " <> unparseType b <> ")" +unparseType (TApp2 TSum a b) = "(" <> unparseType a <> " + " <> unparseType b <> ")" +unparseType (TApp TList a) = "[" <> unparseType a <> "]" +unparseType (TApp a b) = "(" <> unparseType a <> " " <> unparseType b <> ")" +unparseType TAbs = "(->)" +unparseType TProd = "(*)" +unparseType TSum = "(+)" +unparseType TUnit = "★" +unparseType TVoid = "⊥" +unparseType TNat = "Nat" +unparseType TList = "[]" +unparseType TChar = "Char" + +unparseScheme :: Scheme -> Text +unparseScheme (TForall [] t) = unparseType t +unparseScheme (TForall names t) = "∀" <> T.unwords names <> ". " <> unparseType t diff --git a/src/Ivo/Types.hs b/src/Ivo/Types.hs index 65fa19c..e08b9b8 100644 --- a/src/Ivo/Types.hs +++ b/src/Ivo/Types.hs @@ -1,8 +1,9 @@ module Ivo.Types ( module Ivo.Types.Base - , infer + , infer, check ) where +import Ivo.Syntax.Printer import Ivo.Types.Base import Control.Applicative ((<|>)) @@ -99,6 +100,10 @@ j (Case ctrs) = do j (CtrC ctr) = do (t_ret, ts_n) <- ctrTy ctr pure $ foldr (\t_a t_r -> tapp [TAbs, t_a, t_r]) t_ret ts_n +j (Ann () e t_ann) = do + t_ret <- j e + unify t_ret t_ann + pure t_ann j CallCCC = do t_a <- fresh t_b <- fresh @@ -156,3 +161,13 @@ infer e = do s <- solve' c let t' = substitute s t pure $ runReader (generalize t') HM.empty + +check :: Type -> CheckExpr -> Either Text Scheme +check t_ann e = do + (t, c) <- runInferencer do + t_ret <- j e + unify t_ret t_ann + pure t_ann + s <- solve' c + let t' = substitute s t + pure $ runReader (generalize t') HM.empty diff --git a/src/Ivo/Types/Base.hs b/src/Ivo/Types/Base.hs index 1eb426d..145a14a 100644 --- a/src/Ivo/Types/Base.hs +++ b/src/Ivo/Types/Base.hs @@ -1,30 +1,26 @@ -{-# LANGUAGE TemplateHaskell #-} module Ivo.Types.Base ( Identity (..) , Expr (..), Ctr (..), Pat, ExprF (..), PatF (..), VoidF, UnitF (..), Text + , Type (..), TypeF (..), Scheme (..), tapp , substitute, substitute1, rename, rename1, free, bound, used , Check, CheckExpr, CheckExprF, CheckX, CheckXF (..) , pattern AppFC, pattern CtrC, pattern CtrFC, pattern CallCCC, pattern CallCCFC , pattern FixC, pattern FixFC, pattern HoleC, pattern HoleFC - , Type (..), TypeF (..), Scheme (..), tapp , Substitution, Context, Constraint , MonoSubstitutable, substituteMono, substituteMono1 - , unparseType, unparseScheme ) where +import Ivo.Expression.Base + import Control.Monad (forM) import Control.Monad.Reader (asks) import Data.Bifunctor (bimap, first) import Data.Foldable (fold) import Data.Functor.Foldable (embed, cata, para) -import Data.Functor.Foldable.TH (makeBaseFunctor) import Data.Functor.Identity (Identity (..)) import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict qualified as HM -import Data.List (foldl1') -import Data.Text qualified as T import Data.Traversable (for) -import Ivo.Expression.Base data Check type CheckExpr = Expr Check @@ -32,6 +28,7 @@ type instance AppArgs Check = CheckExpr type instance AbsArgs Check = Text type instance LetArgs Check = (Text, CheckExpr) type instance CtrArgs Check = UnitF CheckExpr +type instance AnnX Check = () type instance XExpr Check = CheckX type CheckX = CheckXF CheckExpr @@ -79,15 +76,15 @@ pattern HoleC = ExprX HoleC_ pattern HoleFC :: CheckExprF r pattern HoleFC = ExprXF HoleC_ -{-# COMPLETE Var, App, Abs, Let, CtrC, Case, ExprX #-} -{-# COMPLETE VarF, AppF, AbsF, LetF, CtrFC, CaseF, ExprXF #-} -{-# COMPLETE VarF, AppFC, AbsF, LetF, CtrF, CaseF, ExprXF #-} -{-# COMPLETE VarF, AppFC, AbsF, LetF, CtrFC, CaseF, ExprXF #-} -{-# COMPLETE Var, App, Abs, Let, Ctr, Case, CallCCC, FixC, HoleC #-} -{-# COMPLETE Var, App, Abs, Let, CtrC, Case, CallCCC, FixC, HoleC #-} -{-# COMPLETE VarF, AppF, AbsF, LetF, CtrFC, CaseF, CallCCFC, FixFC, HoleFC #-} -{-# COMPLETE VarF, AppFC, AbsF, LetF, CtrF, CaseF, CallCCFC, FixFC, HoleFC #-} -{-# COMPLETE VarF, AppFC, AbsF, LetF, CtrFC, CaseF, CallCCFC, FixFC, HoleFC #-} +{-# COMPLETE Var, App, Abs, Let, CtrC, Case, Ann, ExprX #-} +{-# COMPLETE VarF, AppF, AbsF, LetF, CtrFC, CaseF, AnnF, ExprXF #-} +{-# COMPLETE VarF, AppFC, AbsF, LetF, CtrF, CaseF, AnnF, ExprXF #-} +{-# COMPLETE VarF, AppFC, AbsF, LetF, CtrFC, CaseF, AnnF, ExprXF #-} +{-# COMPLETE Var, App, Abs, Let, Ctr, Case, Ann, CallCCC, FixC, HoleC #-} +{-# COMPLETE Var, App, Abs, Let, CtrC, Case, Ann, CallCCC, FixC, HoleC #-} +{-# COMPLETE VarF, AppF, AbsF, LetF, CtrFC, CaseF, AnnF, CallCCFC, FixFC, HoleFC #-} +{-# COMPLETE VarF, AppFC, AbsF, LetF, CtrF, CaseF, AnnF, CallCCFC, FixFC, HoleFC #-} +{-# COMPLETE VarF, AppFC, AbsF, LetF, CtrFC, CaseF, AnnF, CallCCFC, FixFC, HoleFC #-} instance RecursivePhase Check where projectAppArgs = Identity @@ -125,66 +122,6 @@ instance Substitutable CheckExpr where CaseF pats -> Case <$> for pats \(Pat ctr ns e) -> Pat ctr ns <$> maySubstitute ns e e -> embed <$> traverse snd e --- | A monomorphic type. -data Type - -- | Type variable. - = TVar Text - -- | Type application. - | TApp Type Type - -- | The function type. - | TAbs - -- | The product type. - | TProd - -- | The sum type. - | TSum - -- | The unit type. - | TUnit - -- | The empty type. - | TVoid - -- | The type of natural numbers. - | TNat - -- | The type of lists. - | TList - -- | The type of characters. - | TChar - deriving (Eq, Show) - -makeBaseFunctor ''Type - -instance Substitutable Type where - collectVars withVar _ = cata \case - TVarF n -> withVar n - t -> fold t - - -- /All/ variables in a monomorphic type are free. - rename _ t = t - - -- No renaming step is necessary. - substitute substs = cata \case - TVarF n -> HM.findWithDefault (TVar n) n substs - e -> embed e - - unsafeSubstitute = substitute - --- | A polymorphic type. -data Scheme - -- | Universally quantified type variables. - = TForall [Text] Type - deriving (Eq, Show) - -instance Substitutable Scheme where - collectVars withVar withBinder (TForall names t) = - foldMap withBinder names $ collectVars withVar withBinder t - - rename = runRenamer \badNames (TForall names t) -> - uncurry TForall <$> replaceNames badNames names (pure t) - - -- I took a shot at implementing this but found it to be quite difficult - -- because merging the foralls is tricky. - -- It's not undoable, but it wasn't worth my further time investment - -- seeing as this function isn't currently used anywhere. - unsafeSubstitute = error "Substitution for schemes not yet implemented" - type Substitution = HashMap Text Type type Context = HashMap Text Scheme type Constraint = (Type, Type) @@ -210,33 +147,3 @@ instance MonoSubstitutable t => MonoSubstitutable [t] where instance MonoSubstitutable t => MonoSubstitutable (HashMap a t) where substituteMono = fmap . substituteMono - -tapp :: [Type] -> Type -tapp [] = error "Empty type applications are not permitted" -tapp [t] = t -tapp ts = foldl1' TApp ts - --- HACK -pattern TApp2 :: Type -> Type -> Type -> Type -pattern TApp2 tf tx ty = TApp (TApp tf tx) ty - --- TODO: Improve these printers. -unparseType :: Type -> Text -unparseType (TVar name) = name -unparseType (TApp2 TAbs a b) = "(" <> unparseType a <> " -> " <> unparseType b <> ")" -unparseType (TApp2 TProd a b) = "(" <> unparseType a <> " * " <> unparseType b <> ")" -unparseType (TApp2 TSum a b) = "(" <> unparseType a <> " + " <> unparseType b <> ")" -unparseType (TApp TList a) = "[" <> unparseType a <> "]" -unparseType (TApp a b) = "(" <> unparseType a <> " " <> unparseType b <> ")" -unparseType TAbs = "(->)" -unparseType TProd = "(*)" -unparseType TSum = "(+)" -unparseType TUnit = "★" -unparseType TVoid = "⊥" -unparseType TNat = "Nat" -unparseType TList = "[]" -unparseType TChar = "Char" - -unparseScheme :: Scheme -> Text -unparseScheme (TForall [] t) = unparseType t -unparseScheme (TForall names t) = "∀" <> T.unwords names <> ". " <> unparseType t